0% found this document useful (0 votes)
2 views

C++_Assignment-10

The document contains a programming assignment for Week 10 in Modern C++, consisting of multiple-choice questions and programming tasks related to C++ concepts such as templates, constexpr, and operator overloading. Each question includes code snippets, explanations, and correct answers. The assignment is designed to test the understanding of advanced C++ features and their applications.

Uploaded by

Amitanshu Vines
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

C++_Assignment-10

The document contains a programming assignment for Week 10 in Modern C++, consisting of multiple-choice questions and programming tasks related to C++ concepts such as templates, constexpr, and operator overloading. Each question includes code snippets, explanations, and correct answers. The assignment is designed to test the understanding of advanced C++ features and their applications.

Uploaded by

Amitanshu Vines
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

Programming in Modern C++: Assignment Week 10

Total Marks : 25

Partha Pratim Das


Department of Computer Science and Engineering
Indian Institute of Technology Kharagpur, Kharagpur – 721302
[email protected]

Question 1
Consider the code segment (in C++11) given below. [MSQ, Marks 2]

#include<iostream>
int main( ){
char n = 'A';
char& rn = n;
___________ t = rn; //LINE-1

++t;
std::cout << n << " " << rn << " " << t << std::endl;
return 0;
}

Identify the appropriate option/s to fill in the blank at LINE-1 such that output becomes B B
B

a) auto

b) auto&

c) decltype(rn)

d) decltype(n)

Answer: b), c)
Explanation:
Since auto never deduces adornments like cv-qualifer or reference, in option a) the inferred
type of t is char. Thus, output will be A A B.
In option b) the inferred type of t is char&. Thus, output will be B B B.
In option c) the inferred type of t is the type of rn that is char&. Thus, output will be B B B.
In option d) the inferred type of t is the type of n that is char. Thus, output will be A A B.

1
Question 2
Consider the code segment (in C++14) given below. [MSQ, Marks 2]

#include<iostream>
struct Oper1{
int i;
Oper1(int _i) : i(_i){}
int& operator()() { std::cout << "1 "; return i ; }
};

struct Oper2{
int i;
Oper2(int _i) : i(_i){}
int operator()() { std::cout << "2 "; return i ; }
};

template < typename U >


__________________ { //LINE-1
return op() ;
}

int main(){
Oper1 o1{10};
Oper2 o2{10};
foobar(o1) = 20;
foobar(o2) ;
return 0;
}

Identify the appropriate option/s to fill in the blank at LINE-1 such that output becomes 1 2.

a) auto foobar( U& op ) -> decltype(op())

b) auto foobar( U& op )

c) auto& foobar( U& op )

d) decltype(auto) foobar( U& op )

Answer: a), d)
Explanation:
The call foobar(o1) = 20; evaluates to lvalue of type int&.
The call foobar(o2); evaluates to prvalue of type int.
Since plain auto never deduces to a reference, option b) fails for prvalue.
Since plain auto& always deduces to a reference, option b) fails for prvalue.
Option a) and d) works for lvalue as well as prvalue. Thus these two are correct options.

2
Question 3
Consider the following class (int C++11). [MCQ, Marks 2]

class CustList{
public:
CustList(std::initializer_list<double> dlist) { cout << "ctor-1" << " "; }
CustList(std::initializer_list<int> dlist) { cout << "ctor-2" << " "; }
CustList(double d1, double d2, double d3) { cout << "ctor-3" << " "; }
};

Indemnify the appropriate option that present all correct output/error for the following in-
stantiation of CustList class:

1. CustList c{3.1, 4.5, 6.5};

2. CustList c(3.1, 4.5, 6.5);

3. CustList c{3.1f, 4.5f, 6.5};

4. CustList c{3, 4.5, 6};

a) (a) ctor-1
(b) ctor-3
(c) ctor-1
(d) ctor-3

b) (a) ctor-1
(b) ctor-3
(c) ctor-1
(d) compiler error: call of overloaded ‘CustList()’ is ambiguous for CustList
c{3, 4.5, 6};

c) (a) ctor-1
(b) ctor-1
(c) ctor-1
(d) compiler error: call of overloaded ‘CustList()’ is ambiguous for CustList
c{3, 4.5, 6};

d) (a) ctor-3
(b) ctor-1
(c) compiler error: call of overloaded ‘CustList()’ is ambiguous for CustList
c{3.1f, 4.5f, 6.5};
(d) compiler error: call of overloaded ‘CustList()’ is ambiguous for CustList
c{3, 4.5, 6};

Answer: b)
Explanation:
The statement CustList c{3.1, 4.5, 6.5}; invokes constructor:

CustList(std::initializer_list<double> dlist);

The statement CustList c(3.1, 4.5, 6.5); invokes constructor:

3
CustList(std::initializer_list<int> dlist);

Since conversion float => double is better than double => int the statement CustList
c{3.1f, 4.5f, 6.5}; invokes constructor:

CustList(double d1, double d2, double d3)

Since the int => double and double => int have the same rank the call CustList c{3,
4.5, 6}; is ambiguous.

4
Question 4
Consider the following code segment. [MSQ, Marks 2]

#include <iostream>

class ComplexNum{
public:
constexpr ComplexNum(int _r = 0, int _i = 0) : r(_r), i(_i){ }
private:
int r, i;
};
int randGen(){
return 10;
}
constexpr int numGen(int i, int j){
return i + j;
}
int main(){
constexpr ComplexNum c1(10, 20); //LINE-1
constexpr int i = 10, j = 20;
constexpr ComplexNum c2(i, j); //LINE-2
constexpr ComplexNum c3(randGen(), randGen()); //LINE-3
constexpr ComplexNum c4(numGen(i, j), numGen(i, j)); //LINE-4
return 0;
}

Which of the following line/s generate/s compiler error?

a) LINE-1

b) LINE-2

c) LINE-3

d) LINE-4

Answer: c)
Explanation:
At LINE-1, the constructor parameters are constants from the compile time context. Thus,
the object can be constexpr.
At LINE-2, the constructor parameters are constexpr. Thus, the object can be of constexpr.
At LINE-3, the constructor parameters are the return value of the randGen() function. How-
ever, the return type of randGen() is not constexpr, therefore the object cannot be constexpr.
At LINE-4, the constructor parameters are the return value of the numGen(int, int) func-
tion. However, the return type of numGen(int, int) is constexpr, therefore the object can
be constexpr.
Intentionally made MSQ.

5
Question 5
Consider the following code segment. [MSQ, Marks 2]

#include <iostream>

void update(char* str){ /*some code*/ }

template<typename F, typename P>


void caller(F func, P s){
func(s);
}

int main(){
char s[2] = "0";
char *p = &s[1];
caller(update, p); //LINE-1
caller(update, 0); //LINE-2
caller(update, NULL); //LINE-3
caller(update, nullptr); //LINE-4
return 0;
}

Which of the following lines generate/s compiler error?

a) LINE-1

b) LINE-2

c) LINE-3

d) LINE-4

Answer: b), c)
Explanation:
For the call in LINE-1, the template type parameter P is deduced to char*. Thus, it does not
generate any compiler error.
For the call in LINE-2, the template type parameter P is deduced to int. Thus, it generates a
compiler error.
For the call in LINE-3, the template type parameter P is deduced to long int. Thus, it gen-
erates a compiler error.
For the call in LINE-2, the template type parameter P is deduced to std::nullptr t and the
call update(std::nullptr t) is syntactically correct.

6
Question 6
Consider the following code segment (in C++11). [MCQ, Marks 2]

#include<iostream>
#include<iomanip>

__________________________________________ { //LINE-1
return 1024 * mem;
}

__________________________________________ { //LINE-2
return mem;
}
int main() {
long double size = 10.0_KB + 2.0_B;
std::cout << "size (in bytes): " << size;
return 0;
}

Identify the appropriate option to fill in the blanks at LINE-1 and LINE-2 such that the output
becomes size (in bytes): 10242.

a) LINE-1: long double operator"" KB(long double mem)


LINE-2: long double operator"" B(long double mem)

b) LINE-1: long double operator"" KB(long double mem)


LINE-2: long double operator"" B(long double mem)

c) LINE-1: long int operator"" KB(long int mem)


LINE-2: long int operator"" B(long int mem)

d) LINE-1: unsigned long long operator KB(unsigned long long mem)


LINE-2: unsigned long long operator B(unsigned long long mem)

Answer: b)
Explanation:
Since the user-defined literals in the program are double type option b) is correct.

7
Question 7
Consider the code segment (in C++11) below. [MCQ, Marks 2]

#include<iostream>
#include<vector>
#include<cmath>

void process(int& v) {
if(v < 0)
throw v;
++v;
}

void func(std::vector<int>& iVec) noexcept(noexcept(process(iVec[0]))){


for(int& v : iVec)
process(v);
}

int main() {
std::vector<int> iVec{1, 2, -1, 2};
try{
func(iVec);
}catch(int i){
}
for(int v : iVec)
std::cout << v << " ";
return 0;
}

Identify the correct option about the program above.

a) It generates output as 2 3 0 3

b) It generates output as 2 3 -1 2

c) It generates output as 2 3 -1 3

d) The program gets terminated since a function that is declared noexcept throws an exception

Answer: b)
Explanation:
If we consider the function header:
void func(std::vector<int>& iVec) noexcept(noexcept(process(iVec[0])))
Since the function process(iVec[0]) is not declared as noexcept, noexcept(process(iVec[0]))
is false. Thus the function header is treated as:
void func(std::vector<int>& iVec) noexcept(false)
Therefore, since for functionfunc noexcept is false, it can throw exception. The values 1 2
will become 2 3. However, for -1, it throws exception and control comes out of try block (in
main()).

8
Question 8
Consider the following code segment (in C++11). [MCQ, Marks 2]

#include<iostream>
int i = 10;

void test(int&& rv){ }

int getVal(){
return i;
}

int& getRef(){
return i;
}

int main() {
test(i); //LINE-1
test(i + 10); //LINE-2
test(getVal()); //LINE-3
test(getRef()); //LINE-4
return 0;
}

Identify the line/s generate/s compiler error.

a) LINE-1

b) LINE-2

c) LINE-3

d) LINE-4

Answer: a), d)
Explanation:
The function test(int&&) accepts an integer which is a non-constant rvalue.
At LINE-1, we pass a lvalue i, so it is incorrect.
At LINE-2, i + 10 is a temporary value which is a rvalue. Therefore, the call is correct.
At LINE-3, the function getVal() returns a rvalue. Therefore, the call is correct.
At LINE-4, the function getRef() return a reference which is a lvalue. Therefore, the call is
incorrect.

9
Question 9
Consider the following code segment (in C++ 11). [MCQ, Marks 2]

#include<iostream>

class Resource {
public:
Resource() { std::cout << "#1" << " "; }
Resource(const Resource&) { std::cout << "#2" << " "; }
Resource(Resource&&) noexcept { std::cout << "#3" << " "; }
Resource& operator=(const Resource&) { std::cout << "#4" << " ";
return *this; }
Resource& operator=(Resource&&) noexcept { std::cout << "#5" << " ";
return *this; }
};

Resource createResource(){
Resource r;
return r;
}

int main() {
Resource r1;
r1 = createResource();
Resource r2 = r1;
Resource r3 = std::move(r2);
return 0;
}

What will be the output?

a) #1 #5 #4 #3

b) #1 #1 #5 #4 #3

c) #1 #3 #2 #3

d) #1 #1 #5 #2 #3

Answer: d)
Explanation:
The statement Resource r1; calls default constructor and prints #1.
In the statement r1 = createResource();, the function createResource() calls default con-
structor and prints #1. Then, it calls move assignment constructor and prints #5.
The statement Resource r2 = r1; calls copy constructor and prints #2.
The statement Resource r3 = std::move(r2); move constructor and prints #3.

10
Programming Questions

Question 1
Consider the program below (in C++14).
• Fill in the blanks at LINE-1 and LINE-2 with appropriate namespace declaration.

• Fill in the blanks at LINE-3 with an appropriate statement, such that all symbols defined
in the scope namespace Ver1 becomes available in the main().
The program must satisfy the given test cases. Marks: 3
#include <iostream>
#include <list>
namespace Ver1{
___________________ { //LINE-1
int addAll(std::list<int> i_list){
int sum = 0;
for(auto i : i_list){
sum += i;
}
return sum;
}
}
___________________ { //LINE-2
template<typename T>
T addAll(std::list<T> t_list){
T sum = 0;
for(auto i : t_list){
sum += i;
}
return sum;
}
}
}

______________________ ; //LINE-3
int main(){
int n;
std::cin >> n;
std::list<int> ilist;
std::list<double> dlist;
for(int i = 0; i < n; i++){
int x;
std::cin >> x;
ilist.push_back(x);
}
for(int i = 0; i < n; i++){
double x;
std::cin >> x;
dlist.push_back(x);
}
std::cout << Ver1_1::addAll(ilist) << " ";

11
std::cout << addAll(ilist) << " ";
std::cout << addAll(dlist);
return 0;
}

Public 1
Input:
3
2 4 6
2.1 3.4 5.2
Output:
12 12 10.7

Public 2
Input:
5
3 9 4 8 3
4.3 2.5 6.5 1.2 7.4
Output:
27 27 21.9

Private
Input:
4
7 3 4 5
4.3 2.4 5.6 7.3
Output:
19 19 19.6

Answer:
LINE-1: namespace Ver1 1
LINE-2: inline namespace Ver1 2
LINE-3: using namespace Ver1
Explanation:
Since the integer version function addAll is explicitly called from namespace Ver1 1, the blank
at LINE-1 must be filled in as:
namespace Ver1 1
Since the template version of function addAll is called by default, the blank at LINE-2 must
be filled in as:
inline namespace Ver1 2
Please note that namespace name can be different in this case.
Since we access the namespaces from Ver1 directly in main(), the blank at LINE-3 must be
filled in as:
using namespace Ver1

12
Question 2
Consider the following program (in C++14).
• Fill in the blank at LINE-1 with an appropriate template definition.

• Fill in the blank at LINE-2 with an appropriate header for function divide.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
int getNumber(char c){
return int(c);
}
int getNumber(double d){
return int(d);
}
double getNumber(int i){
return double(i);
}

______________________________ //LINE-1
_____________________________ { //LINE-2
return getNumber(n1) / getNumber(n2);
}
int main(){
int a;
double b;
char c;
std::cin >> a >> b >> c;
std::cout << divide(c, a) << " ";
std::cout << divide(c, b);
return 0;
}

Public 1
Input:
10 2.0 A
Output:
6.5 32

Public 2
Input:
5 5.0 B
Output:
13.2 13

Private
Input:
10 10.5 C
Output:
6.7 6

13
Answer:
In C++11

LINE-1: template<typename T, typename U>


LINE-2: auto divide(T n1, U n2) -> decltype(getNumber(n1) / getNumber(n2))

or in C++14

LINE-2: decltype(auto) divide(T n1, U n2)

Explanation:
Since we can pass two different types of parameters in divide function, we can write at LINE:
template<typename T, typename U>
The header for function divide in C++11 should be:
auto divide(T n1, U n2) -> decltype(getNumber(n1) / getNumber(n2))
And in C++14:
decltype(auto) divide(T n1, U n2)

14
Question 3
Consider the following program that implements copy constructor, copy assignment, move
constructor and move assignment.
• Fill the missing code segments at code-segment-1 to implement the move constructor.

• Fill the missing code segments at code-segment-2 to implement the move assignment.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
class point {
public:
point(int x = 0, int y = 0) : _px(new int(x)), _py(new int(x)) { }
point(const point& p) : _px(new int(*(p._px) * 2)),
_py(new int(*(p._py) * 2)) { }
point& operator=(const point& p) {
if (this != &p) {
delete _px;
delete _py;
_px = new int(*(p._px) * 3);
_py = new int(*(p._py) * 3);
}
return *this;
}
~point() { delete _px; delete _py; }
point(point&& p) noexcept : _px(p._px), _py(p._py) {
//code-segment-1
}
point& operator=(point&& p) noexcept{
//code-segment-2
}
friend std::ostream& operator<<(std::ostream& os, const point& p) {
std::cout << "(" << *(p._px) << ", " << *(p._py) << ")";
return os;
}
friend std::istream& operator>>(std::istream& os, point& p) {
std::cin >> *(p._px) >> *(p._py);
return os;
}
private:
int *_px = nullptr, *_py = nullptr;
};

int main(){
point p1;
std::cin >> p1;
point p2 = p1;
point p3;
p3 = p1;
std::cout << p1 << ", " << p2 << ", " << p3 << std::endl;

point p4 = std::move(p1);

15
std::cout << p4 << ", ";
point p5;
p5 = std::move(p4);
std::cout << p5;
return 0;
}

Public 1
Input: 10 10
Output:
(10, 10), (20, 20), (30, 30)
(40, 40), (200, 200)

Public 2
Input: 1 2
Output:
(1, 2), (2, 4), (3, 6)
(4, 8), (20, 40)

Private
Input: 100 100
Output:
(100, 100), (200, 200), (300, 300)
(400, 400), (2000, 2000)

Answer:

code-segment-1:

*_px *= 4;
*_py *= 4;
p._px = nullptr;
p._py = nullptr;

code-segment-2:

if (this != &p) {
delete _px;
delete _py;
_px = p._px;
_py = p._py;
*_px *= 5;
*_py *= 5;
p._px = nullptr;
p._py = nullptr;
}
return *this;

Explanation:
The implementation of move constructor is as follows:

16
point(point&& p) noexcept : _px(p._px), _py(p._py) {
*_px *= 4;
*_py *= 4;
p._px = nullptr;
p._py = nullptr;
}

The implementation of move assignment is as follows:

point& operator=(point&& p) noexcept{


if (this != &p) {
delete _px;
delete _py;
_px = p._px;
_py = p._py;
*_px *= 5;
*_py *= 5;
p._px = nullptr;
p._py = nullptr;
}
return *this;
}

17

You might also like