C++ 2024H1 Assignment-10
C++ 2024H1 Assignment-10
Total Marks : 25
Question 1
Consider the code segment (in C++11) given below. [MSQ, Marks 2]
#include<iostream>
int main( ){
int n = 10;
int& rn = n;
______ tn = rn; //LINE-1
++tn;
std::cout << n << " " << rn << " " << tn;
return 0;
}
Identify the appropriate option/s to fill in the blank at LINE-1 such that output becomes
10 10 11
a) auto
b) auto&
c) decltype(rn)
d) decltype((n))
Answer: a)
Explanation:
Since auto never deduces adornments like cv-qualifer or reference, in option a) the inferred
type of tn is int. Thus, output will be 10 10 11.
In option b) the inferred type of tn is int&. Thus, output will be 11 11 11.
In option c) the inferred type of tn is the type of rn that is int&. Thus, output will be 11 11
11.
In option d) the inferred type of t is the type of reference to n that is int&. Thus, output will
be 11 11 11.
Intentionally kept as MSQ
1
Question 2
Consider the code segment (in C++11) given below. [MSQ, Marks 2]
#include <iostream>
int main( ){
int arr[] = { 1, 2, 3, 4, 5 };
for(________________ : arr) // LINE-1
i *= 2;
Identify the appropriate option(s) to fill in the blank at LINE-1 such that the output of the
program is NOT: 2 4 6 8 10
a) auto i
b) auto& i
c) decltype(*arr) i
d) decltype(arr[0]) i
Answer: a)
Explanation:
In option a), the inferred type of i is int. Thus, the changes made in i are not reflected in
the array arr. So, the O/P is 1 2 3 4 5
In option b), the inferred type of i is int&. Thus, the changes made in i are reflected in the
array arr.
In option c), the inferred type of i is the output of decltype(*arr) i.e. int&. Thus, the
changes made in i are reflected in the array arr. In option d), the inferred type of i is the
output of decltype(arr[0]) i.e. int&. Thus, the changes made in i are reflected in the array
arr.
Intentionally kept as MSQ.
2
Question 3
Consider the following code segment. [MSQ, Marks 2]
#include <iostream>
class data{
public:
constexpr data(int n = 0) : n_{n} { }
private:
int n_;
};
int main(){
int i = 10;
const int j = 20;
constexpr data d1(1); //LINE-1
constexpr data d2(i); //LINE-2
constexpr data d3(num_gen1()); //LINE-3
constexpr data d4(num_gen2(j)); //LINE-4
return 0;
}
a) LINE-1
b) LINE-2
c) LINE-3
d) LINE-4
Answer: b), c)
Explanation:
At LINE-1, the constructor parameter is constant from the compile time context. Thus, the
object can be constexpr.
At LINE-2, the constructor parameter is non-const. Thus, the object can not be of constexpr.
At LINE-3, the constructor parameter is the return value of the num gen1() function. However,
the return type of num gen1() is not constexpr, therefore the object cannot be constexpr.
At LINE-4, the constructor parameter is the return value of the num gen2(int) function.
However, the return type of it is constexpr, therefore the object can be constexpr.
3
Question 4
Consider the program (in C++11) given below. [MSQ, Marks 2]
#include <iostream>
constexpr double pi = 3.14;
namespace v1_0 {
class circle{
public:
circle(int r) : r_(r) {}
double area(){ return pi * r_ * r_; }
private:
int r_;
};
}
int main(){
____________ c1(10.5); //LINE-1
____________ c2(10.5); //LINE-2
____________ c3(10.5); //LINE-3
std::cout << c1.area() << " " << c2.area() << " " << c3.area();
return 0;
}
Choose the appropriate option to fill in the blanks at LINE-1, LINE-2, and LINE-3 so that the
program runs without any compilation error and produces the output:
314 314 346.185
a) LINE-1: circle
LINE-2: circle<int>
LINE-3: circle<double>
b) LINE-1: v1 0::circle
LINE-2: v2 0::circle<int>
LINE-3: circle<double>
c) LINE-1: v1 0::circle
LINE-2: circle<int>
LINE-3: circle<double>
d) LINE-1: v2 0::circle
LINE-2: v2 0::circle<int>
LINE-3: v2 0::circle<double>
4
Answer: b), c)
Explanation:
Since namespace v2 0 is declared as inline and it defines the templated version of class circle,
the templated class circle can be instantiated with or without namespace resolution. How-
ever, for namespace v1 0 (defines the non-templated version of circle), namespace resolution
is not mandated.
5
Question 5
Consider the following code segment (in C++11). [MSQ, Marks 2]
#include<iostream>
#include<iomanip>
constexpr double cr = 12.0;
Choose the appropriate option to fill in the blank at LINE-1 such that the output is 125IN.
a) 5.0FT, 10.0IN
b) (FT)5, (IN)10
d) 5 FT, 11 IN
Answer: c)
Explanation:
For user-defined numeric literal operators, the correct way to invoke them is to write them as
5.0 FT, 10.0 IN.
All other options are compilation error. Even option d) is wrong as numeric literal operators
require exact type matching.
Intentionally kept as MSQ
6
Question 6
Consider the following program (in C++11). [MCQ, Marks 2]
#include<iostream>
class A{
public:
A(const int& n) { std::cout << "#1 " ; }
A(const A& ob) { std::cout << "#2 " ; }
A(A&& ob) noexcept { std::cout << "#3 " ; }
};
class B : public A {
public:
B(const int& n, const int& m) : A(n) { std::cout << "#4 " ; }
B(const B& ob) : A(ob) { std::cout << "#5 " ; }
B(B&& ob) noexcept : A(ob) { std::cout << "#6 " ; }
};
int main(){
B b1(1, 2);
B b2(b1);
B b3 = std::move(b1);
return 0;
}
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 b1(100, 200) invokes the parameterized constructor of B class, which forwards
the call to the parameterized constructor of A class. It prints #1 #4.
The statement B b2(b1) invokes the copy constructor of B class, which forwards the call to
the copy constructor of A class. It prints #2 #5.
The statement B b3 = std::move(b1); invokes the move constructor of B class, which (how-
ever) forwards the call to the copy constructor of A class. It prints #2 #6.
7
Question 7
Consider the code segment (C++11) given below. [MCQ, Marks 2]
#include <iostream>
#include <initializer_list>
template<typename T>
class items{
public:
items() { std::cout << "cont-1 "; }
items(char c) { std::cout << "cont-2 "; }
items(std::initializer_list<T> elms) { std::cout << "cont-3 "; }
items(char c, std::initializer_list<T> elms) { std::cout << "cont-4 "; }
};
int main(){
items<char> c1(’a’);
items<char> c2({’a’, ’b’, ’c’});
items<char> c3{’a’, ’b’, ’c’};
items<char> c4 = {’a’, ’b’, ’c’};
items<char> c5(’d’, {’a’, ’b’, ’c’});
return 0;
}
Answer: c)
Explanation:
c1(’a’) invokes parameterized constructor items(char c) { ... }.
c2({’a’, ’b’, ’c’)}, c3{’a’, ’b’, ’c’} and c4 = {’a’, ’b’, ’c’} invoke the initializer
list constructor items(std::initializer list<T> elms){ ... }.
c5(’d’, {’a’, ’b’, ’c’}) invokes the mixed constructor
items(char c, std::initializer list<T> elms){ ... }.
8
Question 8
Consider the following code segment. [MSQ, Marks 2]
#include <iostream>
int main(){
char s[2] = "0";
caller(print, s); //LINE-1
caller(print, 0); //LINE-2
caller(print, s[1]); //LINE-3
caller(print, nullptr); //LINE-4
return 0;
}
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 Param is deduced to char*. Thus, it does
not generate any compiler error.
For the call in LINE-2, the template type parameter Param is deduced to int. Thus, it gener-
ates a compiler error.
For the call in LINE-3, the template type parameter Param is deduced to char. Thus, it gen-
erates a compiler error.
For the call in LINE-4, the template type parameter Param is deduced to std::nullptr t and
the call print(std::nullptr t) is syntactically correct.
9
Question 9
Consider the code segment (in C++14) given below. [MSQ, Marks 2]
#include<iostream>
struct double_it{
int n_;
double_it(int n) : n_(2 * n){}
int operator()() { std::cout << "lval "; return n_; }
};
struct triple_it{
int n_;
triple_it(int n) : n_(3 * n){}
int& operator()() { std::cout << "rval "; return n_; }
};
int main(){
double_it o1{10};
triple_it o2{10};
std::cout << wrapper(o1) << " ";
std::cout << (wrapper(o2) = 20);
return 0;
}
Identify the appropriate option/s to fill in the blank at LINE-1 such that output becomes
lval 20 rval 20.
Answer: b), d)
Explanation:
The call wrapper(o1) evaluates to prvalue of type int.
The call wrapper(o2) = 20 evaluates to lvalue of type int&.
Therefore, the function wrapper requires training return type declaration, which can be done
in two ways:
auto wrapper( T& op ) -> decltype(op()) in C++11, and
decltype(auto) wrapper( T& op ) in C++14.
10
Programming Questions
Question 1
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>
___________________________________ //LINE-1
___________________________________ { //LINE-2
return convert(n1) / convert(n2);
}
int main(){
int a, b;
double c, d;
std::cin >> a >> b >> c >> d;
std::cout << divide(a, b) << " ";
std::cout << divide(a, c) << " ";
std::cout << divide(c, d);
return 0;
}
Public 1
Input: 50 40 30 20
Output:
1.25 1.66667 1
Public 2
Input: 10 6 3 2
Output: 1.66667 3.33333 1
Private
Input: 100 50 70 80
Output: 2 1.42857 0
Answer:
In C++11
LINE-1: template<typename T1, typename T2>
LINE-2: auto divide(T1 n1, T2 n2) -> decltype(convert(n1) / convert(n2))
11
or in C++14
Explanation:
Since we can pass two different types of parameters in divide function, we can write at LINE-1:
template<typename T1, typename T2>
The header for function divide in C++11 should be:
auto divide(T1 n1, n2 n2) -> decltype(convert(n1) / convert(n2))
And in C++14:
decltype(auto) divide(T1 n1, T2 n2)
Please any other legal identifier can be used for the typed-parameters.
12
Question 2
Consider the following program (in C++11).
• Fill in the blank at LINE-1 with appropriate header and initialization list for copy con-
structor.
• Fill in the blank at LINE-2 with appropriate header for overloading copy assignment
operator.
• Fill in the blank at LINE-3 with appropriate header and initialization list for move
constructor.
• Fill in the blank at LINE-4 with appropriate header for overloading move assignment
operator.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
class ctype {
public:
ctype() : cp_(nullptr) { }
ctype(char c) : cp_(new char(c)) { }
__________________________ {} // LINE-1: copy constructor
_________________ { // LINE-2: copy assignment
if (this != &ob) {
delete cp_;
cp_ = new char(*(ob.cp_) + 1);
}
return *this;
}
____________________ { // LINE-3: move constructor
ob.cp_ = nullptr;
}
______________ { // LINE-4: 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_ << ", ";
}
~ctype() { delete cp_; }
private:
char* cp_ = nullptr;
};
int main(){
char a;
13
std::cin >> a;
ctype c4 = std::move(c1);
ctype c5; c5 = std::move(c1);
c1.print(); c4.print(); c5.print();
return 0;
}
Public 1
Input: A
Output: A, A, A, moved, A, moved,
Public 2
Input: X
Output: X, X, X, moved, X, moved,
Private
Input: z
Output: z, z, z, moved, z, moved,
Answer:
LINE-1: ctype(const ctype& ob) : cp (new char(*(ob.cp )))
LINE-2: ctype& operator=(const ctype& ob)
LINE-3: ctype(ctype&& ob) : cp (ob.cp )
LINE-4: ctype& operator=(ctype&& ob)
Explanation:
At LINE-1, the header and initialization list for copy constructor can be written as:
ctype(const ctype& ob) : cp (new char(*(ob.cp )))
At LINE-2, the header for overloading copy assignment operator can be written as:
ctype& operator=(const ctype& ob)
At LINE-3, the header and initialization list for move constructor can be written as:
ctype(ctype&& ob) : cp (ob.cp )
At LINE-4, the header for overloading move assignment operator can be written as:
ctype& operator=(ctype&& ob)
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-2 to complete the header for function add.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
class compex_num{
public:
explicit compex_num(double r = 0, double i = 0) : r_(r), i_(i){}
compex_num operator+(double n){
compex_num res;
res.r_ = this->r_ + n;
res.i_ = this->i_;
return res;
}
compex_num operator+(compex_num c){
compex_num res;
res.r_ = this->r_ + c.r_;
res.i_ = this->i_ + c.i_;
return res;
}
friend std::ostream& operator<<(std::ostream& os, const compex_num& c);
private:
double r_, i_;
};
std::ostream& operator<<(std::ostream& os, const compex_num& c){
os << c.r_ << " + j" << c.i_;
return os;
}
________________________ //LINE-1
________________________ { //LINE-2
______________________________; // LINE-3: define new type Tmp
Tmp sum = n1 + n2;
int main(){
int a, b, r1, i1, r2, i2;
std::cin >> a >> b >> r1 >> i1 >> r2 >> i2;
compex_num c1(r1, i1);
compex_num c2(r2, i1);
add(c1, a);
add(c1, c2);
15
add(a, b);
return 0;
}
Public 1
Input: 10 20 30 40 50 -60
Output:
40 + j40
80 + j80
30
Public 2
Input: -1 2 3 4 -2 -4
Output:
2 + j4
1 + j8
1
Private
Input: -1 -3 0 1 -1 1
Output:
-1 + j1
-1 + j2
-4
Answer:
LINE-1: template<typename T, typename U>
Or
LINE-1: template<class T, class U>
LINE-2: void add(T& n1, U& n2)
LINE-3: typedef decltype(n1 + n2) 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 add can be written as:
void add(T& n1, U& n2)
At LINE-3 the new type Tmp can be created as:
typedef decltype(n1 + n2) Tmp
16