0% found this document useful (0 votes)
38 views16 pages

Assignment 10 Solution

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
38 views16 pages

Assignment 10 Solution

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Programming in Modern C++: Assignment Week 10

Total Marks : 27

Partha Pratim Das


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

March 22, 2023

Question 1
Consider the program (in C++11) given below. [MCQ, Marks 2]

#include <iostream>

__________________________ { // LINE-1
double dct = 0.05;
double getDct(double pri){
return pri * dct;
}
}

__________________________ { // LINE-2
double dct = 0.07;
template<typename T>
T getDct(T pri){
return pri * dct;
}
}

int main(){
std::cout << ver1_0::getDct(105.0) << " ";
std::cout << ver2_0::getDct(105) << " " << getDct(105.0);
return 0;
}

Choose the appropriate option to fill in the blanks at LINE-1 and LINE-2 so that the output
becomes
5.25 7 7.35

a) LINE-1: namespace ver1 0


LINE-2: namespace ver2 0

b) LINE-1: namespace ver1 0


LINE-2: inline namespace ver2 0

c) LINE-1: inline namespace ver1 0


LINE-2: namespace ver2 0

1
d) LINE-1: inline namespace ver1 0
LINE-2: inline namespace ver2 0

Answer: b)
Explanation:
As per the output of ver1 0::getDct(105.0) and ver2 0::getDct(105), the ver1 0 and
ver2 0 must have basic namespace definition. However, since getDct(105.0) invoke the func-
tion getDct from ver2 0, ver2 0 must be the default namespace. Thus, at LINE-1 and LINE-2,
we must have:
namespace ver1 0
inline namespace ver2 0

2
Question 2
Consider the program (in C++11) given below. [MCQ, Marks 2]

#include <iostream>

int main( ){
int n1 = 10;
const int n2 = 10;

int& i1 = n1;
const int& i2 = n2;

auto x1 = i1;
auto x2 = i2; //LINE-1

std::cout << ++x1 << " " << ++x2 << " "; //LINE-2
std::cout << i1 << " " << i2;

return 0;
}

What will be the output/error?

a) Compiler error at LINE-1: auto connot deduce to cv-qualifier

b) Compiler error at LINE-2: read-only x2 cannot be modified

c) 11 11 11 11

d) 11 11 10 10

Answer: d)
Explanation:
Since auto never deduces adornments like cv-qualifer or reference (however, no error or excep-
tion is generated), the inferred type of x1 and x2 is int. Thus, the changes in x1 and x2 are
not reflected on i1 and i2.

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

#include <iostream>
#include <vector>

void change(std::vector<int>& iv){


auto j = 10;
for(_______________ : iv) //LINE-1
i += j;
}
int main( ){
std::vector<int> iVec { 10, 20, 30, 40 };
change(iVec);
for(auto i : iVec)
std::cout << i << ", ";
return 0;
}

Choose the appropriate option/s to fill in the blank at LINE-1 such that the output is
20, 30, 40, 50,

a) auto i

b) decltype(j) i

c) decltype((j)) i

d) decltype(iv[j]) i

Answer: c), d)
Explanation:
In option a) and b) the inferred type of i is int. Thus, the changes made in i are not reflected
in the vector iVec. So, the O/P is 20, 30, 40, 50,
In option c) and d) the inferred type of i is int&. Thus, the changes made in i are reflected
in the vector iVec.

4
Question 4
Consider the following code segment (in C++14). [MSQ, Marks 2]

#include<iostream>

struct LFunc{
int i {10};
int operator()() { return i ; }
};

struct RFunc{
int i {10};
int& operator()() { return i ; }
};

template < typename T >


__________________________ { //LINE-1
return rf() ;
}

int main(){
LFunc f1;
RFunc f2;
std::cout << caller(f1) << " ";
std::cout << (caller(f2) = 20);
return 0;
}

Choose the appropriate option/s to fill in the blank at LINE-1 such that the output is 10 20.

a) auto caller( T& rf )

b) auto caller( T& rf ) -> decltype(rf())

c) int& caller( T& rf )

d) decltype(auto) caller( T& rf )

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

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

#include <iostream>

class point{
public:
constexpr point(int x = 0, int y = 0) : x_(x), y_(y){ }
private:
int x_, y_;
};
int genN(){
return 10;
}
constexpr int genN(int i, int j){
return i + j;
}
int main(){
constexpr point p1 {100, 200}; //LINE-1
constexpr int i = 10;
int j = 20;
constexpr point p2 {i, j}; //LINE-2
constexpr point p3(genN(), genN()); //LINE-3
constexpr point p4(genN(i, j), genN(i, j)); //LINE-4
return 0;
}

Identify the function call/s that will compile without generating any error.

a) LINE-1

b) LINE-2

c) LINE-3

d) LINE-4

Answer: a)
Explanation:
Since the constructor of class point is constexpr type, it accepts all the parameters that
are constants.
At LINE-1, the actual parameters are compile-time constants. Therefore, options a) is correct.
At LINE-2 the actual parameters are variables. Likewise, at LINE-3 the actual parameters are
the return values of genN function that returns non-constant values. Therefore, options b) and
c) generate compiler errors.
Since genN(int i, int j) is constexpr type, it accepts all the parameters that are constants.
Therefore, LINE-4 also generates compiler error.
Intentionally kept as MSQ.

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

#include<iostream>
#include<iomanip>

long double operator"" _KM(long double x) {


return x * 1000;
}
long double operator"" _M(long double x) {
return x;
}
class distance{
public:
distance(int d1, int d2) : d1_(d1), d2_(d2){}
int getDistance(){ return d1_ + d2_; }
private:
int d1_, d2_;
};
int main() {
distance d(_______________________); //LINE-1
std::cout << d.getDistance() << "M";
return 0;
}

Choose the appropriate option to fill in the blank at LINE-1 such that the output is 5011M.

a) 5.0KM, 11.0M

b) 5.0 KM, 11.0 M

c) (KM)5.0, (M)11.0

d) 5 KM, 11 M

Answer: b)
Explanation:
For user-defined numeric literal operators, the correct way to invoke them is to write them as
5.0 KM, 11.0 M.
All other options are compilation error. Even option d) is wrong as numeric literal operators
require exact type matching.
Intentionally kept as MSQ

7
Question 7
Consider the following program (in C++11). [MCQ, Marks 2]

#include<iostream>

class base{
public:
base(const int& x) { std::cout << "#1 " ; }
base(const base& ob) { std::cout << "#2 " ; }
base(base&& ob) noexcept { std::cout << "#3 " ; }
};

class derived : public base {


public:
derived(const int& x, const int& y) : base(x) { std::cout << "#4 " ; }
derived(const derived& ob) : base(ob) { std::cout << "#5 " ; }
derived(derived&& ob) noexcept : base(ob) { std::cout << "#6 " ; }
};

int main(){
derived o1(100, 200);
derived o2(o1);
derived o3(std::move(o1));
return 0;
}

What will be the output?

a) #1 #4 #2 #5 #3 #6

b) #1 #4 #2 #5 #2 #6

c) #1 #4 #2 #5 #3 #5

d) #1 #4 #2 #6 #2 #6

Answer: b)
Explanation:
The statement o1(100, 200) invokes the parameterized constructor of derived class, which
forwards the call to the parameterized constructor of base class. It prints #1 #4.
The statement derived o2(o1) invokes the copy constructor of derived class, which forwards
the call to the copy constructor of base class. It prints #2 #5.
The statement o3(std::move(o1)) invokes the move constructor of derived class, which
(however) forwards the call to the copy constructor of base class. It prints #2 #6.

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

#include <iostream>
#include <list>
#include <initializer_list>

class items{
public:
items(int n) { std::cout << "#1 " ; }
items(std::initializer_list<int> vals) { std::cout << "#2 " ; }
items(int n, std::initializer_list<int> vals) { std::cout << "#3 " ; }
};
int main(){
items i1(10); //LINE-1
items i2{10, 20, 30}; //LINE-2
items i3({10}); //LINE-3
items i4{10}; //LINE-4
items i5 = {10}; //LINE-5
items i6(10, {10, 20, 30}); //LINE-6
return 0;
}

What will be the output?

a) #1 #2 #1 #1 #1 #3

b) #1 #2 #1 #1 #1 #2

c) #1 #2 #1 #2 #2 #3

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

Answer: d)
Explanation:
The statement at LINE-1 invokes the constructor items(int n). However, all the statements
at LINE-2, LINE-3, LINE-4 and LINE-5 invoke the constructor items(std::initializer list<int>
vals). The statement at LINE-6 invokes the constructor items(int n, std::initializer list<int>
vals).

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

#include <iostream>

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

template<typename FUNC, typename PARA>


void wrapper(FUNC f, PARA p){
f(p);
}

int main(){
char s[4] = "C++";
wrapper(print, s); //LINE-1
wrapper(print, 0); //LINE-2
wrapper(print, s[4]); //LINE-3
wrapper(print, nullptr); //LINE-4
return 0;
}

Choose the call/s to wrapper function that will result in compiler error/s.

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 PARA is deduced to char*. Thus, it does
not generate any compiler error.
For the call in LINE-2, the template type parameter PARA is deduced to int. Thus, it generates
a compiler error.
For the call in LINE-3, the template type parameter PARA is deduced to char. Thus, it generates
a compiler error.
For the call in LINE-4, the template type parameter PARA is deduced to std::nullptr t and
the call print(f, std::nullptr t) is syntactically correct.

10
Programming Questions

Question 1
Consider the following program (in C++11).

• Fill in the blank at LINE-1 with appropriate header for overloading copy assignment
operator.

• Fill in the blank at LINE-2 with appropriate header and initialization list for move
constructor.

• Fill in the blank at LINE-3 with appropriate header for overloading move assignment
operator.

The program must satisfy the sample input and output. Marks: 3

#include <iostream>
#include <vector>

class item {
public:
item() : cp_(nullptr) { }
item(char c) : cp_(new char(c)) { }
_______________________________ { // LINE-1: copy assignment
if (this != &ob) {
delete cp_;
cp_ = new char(*(ob.cp_) + 1);
}
return *this;
}
_______________________________ { // LINE-2: move constructor
ob.cp_ = nullptr; }
_______________________________ { // LINE-3: move assignment
if (this != &ob) {
cp_ = ob.cp_;
ob.cp_ = nullptr;
}
return *this;
}
void print(){
if(cp_ == nullptr)
std::cout << "moved, ";
else
std::cout << *cp_ << ", ";
}
~item() { delete cp_; }
private:
char* cp_ = nullptr;
};

11
int main(){
char a;
std::cin >> a;
item i1(a);
item i2;
i2 = i1;
i1.print();
i2.print();

item i3 = std::move(i1);
item i4;
i4 = std::move(i1);
i1.print();
i3.print();
i4.print();

return 0;
}

Public 1
Input: X
Output: X, Y, moved, X, moved,

Public 2
Input: a
Output: a, b, moved, a, moved,

Private
Input: H
Output: H, I, moved, H, moved,

Answer:
LINE-1: item& operator=(const item& ob)
LINE-2: item(item&& ob) : cp (ob.cp )
LINE-3: item& operator=(item&& ob)
Explanation:
At LINE-1, the header for overloading copy assignment operator can be written as:
item& operator=(const item& ob)
At LINE-2, the header and initialization list for move constructor can be written as:
item(item&& ob) : cp (ob.cp )
At LINE-3, the header for overloading move assignment operator can be written as:
item& operator=(item&& ob)

12
Question 2
Consider the following program (in C++11). Fill in the blanks as per the instructions given
below:
• Fill in the blanks at LINE-1 and LINE-2 with appropriate headers for the definition of
function getValue() belongs to class Gram and class KiloGram.
• Fill in the blank at LINE-3 with appropriate template definition.
• Fill in the blank at LINE-4 with appropriate header for function convert weight.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>

class KiloGram;
class Gram{
public:
Gram(double w) : w_(w) { }
KiloGram getValue();
void print(){ std::cout << w_ << "G "; }
private:
double w_;
};

class KiloGram{
public:
KiloGram(double w) : w_(w) { }
Gram getValue();
void print(){ std::cout << w_ << "KG "; }
private:
double w_;
};

________________________ { return KiloGram(w_ / 1000); } //LINE-1


________________________ { return Gram(w_ * 1000); } //LINE-2
________________________ //LINE-3
________________________ { //LINE-4
return w.getValue();
}

int main(){
double a, b;
std::cin >> a >> b;
Gram o1(a);
KiloGram o2(b);
convert_weight(o1).print();
convert_weight(o2).print();
return 0;
}

Public 1
Input: 100 100

13
Output: 0.1KG 100000G

Public 2
Input: 2000 20
Output: 2KG 20000G

Private
Input: 500 500
Output: 0.5KG 500000G

Answer:
LINE-1: KiloGram Gram::getValue()
LINE-2: Gram KiloGram::getValue()
LINE-3: template <typename T>
Or
LINE-3: template <class T>
LINE-4: auto convert weight(T w) -> decltype(w.getValue())
Or
LINE-4: decltype(auto) convert weight(T w)
Explanation:
At LINE-1 and LINE-2 the header for getValue() function can be written as:
KiloGram Gram::getValue()
and
Gram KiloGram::getValue()
At LINE-3 the template, and at LINE-4 the header for function convert weight can be written
as:
template <typename T> or template <class T>
and
auto convert weight(T w) -> decltype(w.getValue()) in C++11/14
or
decltype(auto) convert weight(T w) in C++14

14
Question 3
Consider the following program (in C++11). Fill in the blanks as per the instructions given
below:
• Fill in the blank at LINE-1 with an appropriate template definition.
• Fill in the blank at LINE-2 to complete the header for function inner product.
• Fill in the blank at LINE-3 to define the new type Tmp.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
#include <vector>

_______________________________ //LINE-1
void inner_product(_________________) { //LINE-2
__________________________________; // LINE-3: define new type Tmp
/* Don't edit the following part */
Tmp sum = 0;

for (int i=0; i < v1.size(); ++i) {


sum += v1[i] * v2[i];
}
std::cout << sum << " ";
}

int main(){
float a;
int b;
double c;
std::vector<float> fVec;
std::vector<int> iVec;
std::vector<double> dVec;
for(int i = 0; i < 3; i++) {
std::cin >> a;
fVec.push_back(a);
}
for(int i = 0; i < 3; i++) {
std::cin >> b;
iVec.push_back(b);
}
for(int i = 0; i < 3; i++) {
std::cin >> c;
dVec.push_back(c);
}
inner_product(fVec, iVec);
inner_product(dVec, fVec);
return 0;
}

Public 1
Input:

15
1.5 2.5 3.5
1 2 3
1.2 2.2 3.2
Output: 17 18.5

Public 2
Input:
5.5 3.0 4.5
11 12 13
10.5 11.5 12.5
Output: 155 148.5

Private
Input:
3.14 3.15 3.15
10 20 30
10.11 11.11 12.11
Output: 188.9 104.888

Answer:
LINE-1: template<typename T, typename U>
Or
LINE-1: template<class T, class U>
LINE-2: std::vector<T>& v1, std::vector<U>& v2
LINE-3: typedef decltype(v1[0] * v2[0]) Tmp
Explanation:
At LINE-1 the appropriate template can be defined as:
template<typename T, typename U>
or template<class T, class U>
At LINE-2 the header for function inner product can be written as:
void inner product(std::vector<T>& v1, std::vector<U>& v2)
At LINE-3 the new type Tmp can be created as:
typedef decltype(v1[0] * v2[0]) Tmp

16

You might also like