Why the first copy constructor is called in the code below ? - c++

Why the B(B&) ctor is called, instead of B(const B&), in the construction of object b1 ?
#include <iostream>
using namespace std;
struct B
{
int i;
B() : i(2) { }
B(B& x) : i(x.i) { cout << "Copy constructor B(B&), i = " << i << endl; }
B(const B& x) : i(x.i) { cout << "Copy constructor B(const B&), i = " << i << endl; }
};
int main()
{
B b;
B b1(b);
}

This is because overload resolution applies, and since the argument to the constructor of b1 is b, and b happens to be non-const lvalue, then the constructor taking non-const lvlalue is selected. And that's the first one. Interestingly, both are copy constructors, but your code would be equaly valid with just the latter one.

Because b is not const. Therefore, it matches the first copy ctor perfectly, so that's what the compiler uses.

13.3.3.2/3 says
Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one
of the following rules apply:
— Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if :
S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same
type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is
more cv-qualified than the type to which the reference initialized by S1 refers. [Example:
int f(const int &);
int f(int &);
...
int i;
int j = f(i); // calls f(int&)
In your case since the argument is non-const, the non-const version of the copy c-tor is chosen because it is a better match.

because b is not a constant.

Try this:
int main() {
const B b;
B b1(b);
}
Also, it's a hard decision wheter you should use const or not ;)

Related

friend functions with const parameters

I got to know that to make a friend function, friend function should be explicitly declared in enclosing scope or take an argument of its class. However, this one seems to be a caveat, I am failed to understand. Why does the call to f1(99) not works?
class X {
public:
X(int i) {
std::cout << "Ctor called" << std::endl;
}
friend int f1(X&);
friend int f2(const X&);
friend int f3(X);
};
int f1(X& a) {
std::cout << "non-const called" << std::endl;
}
int f2(const X& a) {
std::cout << "const called" << std::endl;
}
int f3(X a) {
std::cout << "object called" << std::endl;
}
int main() {
f1(99);
f2(99);
f3(99);
}
You're calling the functions with argument 99; but all the functions expect an X. 99 could convert to X implicitly, but the converted X is a temporary object and can't be bound to lvalue-reference to non-const.
The temporary object could be bound to lvalue-reference to const (and also rvalue-reference since C++11), then f2(99); works. And it could be copied to the parameter of f3, then f3(99) works too.
The effects of reference initialization are:
Otherwise, if the reference is lvalue reference to a non-volatile const-qualified type or rvalue reference (since C++11):
Otherwise, object is implicitly converted to T. The reference is bound to the result of the conversion (after materializing a temporary) (since C++17). If the object (or, if the conversion is
done by user-defined conversion, the result of the conversion
function) is of type T or derived from T, it must be equally or less
cv-qualified than T, and, if the reference is an rvalue reference, must not be an lvalue (since C++11).

Constructor being called multiple times

I wrote the following c++ code trying to understand copy elision in c++.
#include <iostream>
using namespace std;
class B
{
public:
B(int x ) //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob =5;
ob=6;
ob=7;
return 0;
}
This produces the following output:
Constructor called
Constructor called
Constructor called
I fail to understand why is the constructor being called thrice with each assignment to object ob.
B ob =5;
This uses the given constructor.
ob=6;
This uses the given constructor because there is not a B& operator=(int) function and 6 must be converted to type B. One path to do this is to temporarily construct a B and use it in the assignment.
ob=7;
Same answer as above.
I fail to understand why is the constructor being called thrice with each assignment
As I stated above you do not have a B& operator=(int) function but the compiler is happy to provide a copy assignment operator (i.e., B& operator=(const B&);) for you automatically. The compiler generated assignment operator is being called and it takes a B type and all int types can be converted to a B type (via the constructor you provided).
Note: You can disable the implicit conversion by using explicit (i.e., explicit B(int x);) and I would recommend the use of explicit except when implicit conversions are desired.
Example
#include <iostream>
class B
{
public:
B(int x) { std::cout << "B ctor\n"; }
B(const B& b) { std::cout << B copy ctor\n"; }
};
B createB()
{
B b = 5;
return b;
}
int main()
{
B b = createB();
return 0;
}
Example Output
Note: Compiled using Visual Studio 2013 (Release)
B ctor
This shows the copy constructor was elided (i.e., the B instance in the createB function is triggered but no other constructors).
Each time you assign an instance of the variable ob of type B an integer value, you are basically constructing a new instance of B thus calling the constructor. Think about it, how else would the compiler know how to create an instance of B if not through the constructor taking an int as parameter?
If you overloaded the assignment operator for your class B taking an int, it would be called:
B& operator=(int rhs)
{
cout << "Assignment operator" << endl;
}
This would result in the first line: B ob = 5; to use the constructor, while the two following would use the assignment operator, see for yourself:
Constructor called
Assignment operator
Assignment operator
http://ideone.com/fAjoA4
If you do not want your constructor taking an int to be called upon assignment, you can declare it explicit like this:
explicit B(int x)
{
cout << "Constructor called" << endl;
}
This would cause a compiler error with your code, since it would no longer be allowed to implicitly construct an instance of B from an integer, instead it would have to be done explicitly, like this:
B ob(5);
On a side note, your constructor taking an int as parameter, is not a default constructor, a default constructor is a constructor which can be called with no arguments.
You are not taking the assignment operator into account. Since you have not defined your own operator=() implementation, the compiler generates a default operator=(const B&) implementation instead.
Thus, your code is effectively doing the following logic:
#include <iostream>
using namespace std;
class B
{
public:
B(int x) //custom constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
B& operator=(const B &b) //default assignment operator
{
return *this;
}
};
int main()
{
B ob(5);
ob.operator=(B(6));
ob.operator=(B(7));
return 0;
}
The compiler-generated operator=() operator expects a B object as input, but you are passing an int value instead. Since B has a non-explicit constructor that accepts an int as input, the compiler is free to perform an implicit conversion from int to B using a temporary object.
That is why you are seeing your constructor invoked three times - the two assignments are creating temporary B objects.

Direct object initialization vs Initialization with convertion functions

The following program prints 42:
#include <iostream>
struct A
{
operator int(){ return 42; }
};
struct B
{
operator A(){ return A(); }
};
B b;
int a = A(b);
int main(){ std::cout << a << std::endl; } //42
DEMO
But if we try to define cope/move or both contructors it won't work.
#include <iostream>
struct A
{
A(A&&){ std::cout << "A(A&&)" << std::endl; }
A(A&){ std::cout << "A(A&)" << std::endl; }
operator int(){ return 42; }
};
struct B
{
operator A(){ return A(); }
};
B b;
int a = A(b);
int main(){ std::cout << a << std::endl; } //Error
DEMO
I thought, the relevant section, describing that behavior is N4296::8.5/17.7 [dcl.init]
If the destination type is a (possibly cv-qualified) class type:
[...]
— Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion
functions are enumerated (13.3.1.5), and the best one is chosen
through overload resolution (13.3). The user-defined conversion so
selected is called to convert the initializer expression into the
object being initialized. If the conversion cannot be done or is
ambiguous, the initialization is ill-formed.
It shouldn't depends on absence/presence of the constructors. We just should have to have appropriate conversion functions so as to choose the conversion sequence.
You effectively deleted the default constructor. From the standard (12.1/4, emphasis mine):
A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared
as defaulted
IF there is no user-declared constructor. But you declared two, so there is no implicit default constructor. Thus, this:
operator A(){ return A(); }
// ^^^
can't compile. That's why the error you get is
error: no matching function for call to A::A()
The code tries to call your conversion operator - but the body isn't valid.

Cannot convert the second argument of the initializer-list

The Standard says: (N3797::13.3.3.1/4 [over.best.ics])
However, when considering the argument of a constructor or
user-defined conversion function that is a candidate by 13.3.1.3 when
invoked for the copying/moving of the temporary in the second step of
a class copy-initialization, by 13.3.1.7 when passing the initializer
list as a single argument or when the initializer list has exactly one
element and a conversion to some class X or reference to (possibly
cv-qualified) X is considered for the first parameter of a constructor
of X, [...] only standard conversion sequences and ellipsis conversion sequences are considered.
That's we're concerned about the first argument only in the following case:
#include <iostream>
struct B;
struct C
{
C(){ }
C(const B&){ }
};
struct B
{
B(){ }
B(const C&){ }
};
struct A
{
A(const C&, const C&){ }
A(const B&){ }
};
B b;
C c;
A a{b, b};
int main(){ }
DEMO
It works, although I provided b as the first argument and the user-defined conversion B-->C shouldn't be applied.
You have a conversion from a B to an A and a conversion from a C to a B but you are trying to construct an A from {B,C} and there is no constructor of that form. . .
But now you've chanded it completely. :)

C++ Constructor called from function by reference

I was doing some C++ test and I don't understand the code below:
class A
{
public:
A(int n = 0): m_n(n)
{
std::cout << 'd';
}
A(const A& a): m_n(a.m_n)
{
std::cout << 'c';
}
private:
int m_n;
};
void f(const A &a1)
{
}
int main()
{
//Call 'f' function and prints: d
f(3);
return 0;
}
What I don't understand is why the constructor is called here and prints 'd'?
Thank you.
You are passing an int value to a function that wants a reference to an A value. In order to provide that argument, a temporary A is created using the conversion constructor A(int), printing 'd'.
The temporary is destroyed at the end of the expression statement, after the function returns.
Note that this only work if the reference is either const, or an rvalue reference. Temporary values can't bind to non-const lvalue references; so, if the argument type were A&, then you should get an error.
It also requires that the conversion can be done implicitly; you could prevent this conversion by declaring the constructor explicit.
A(int n = 0): m_n(n)
{
std::cout << 'd';
}
This is a conversion constructor. When the function needs a parameter of type A but a int variable is supplied, it'll be implicitly converted to A using this conversion constructor.
To avoid this implicit conversion, you can add explicit specifier to your constructor.
explicit A(int n = 0): m_n(n)
{
std::cout << 'd';
}