C++ Constructor called from function by reference - c++

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';
}

Related

Why are temporary strings created here when returned as const char* ? [Stroustrup's book example]

I'm trying to understand below code from the beginner's book. (edit: 'programming principles and practice using c++' page 1085) I don't quite get why temporary strings are made according to the comments.
const char* string_tbl[ ] = { "Mozart", "Grieg", "Haydn", "Chopin" };
const char* f(int i) { return string_tbl[i]; }
void g(string s){}
void h()
{
const string& r = f(0); // bind temporary string to r
g(f(1)); // make a temporary string and pass it
string s = f(2); // initialize s from temporary string
cout << "f(3): " << f(3) // make a temporary string and pass it
<<" s: " << s
<< " r: " << r << '\n';
}
f() returns a pointer to const char, right?
Shouldn't const string& r = f(0); assign a 'pointer to char' (in this case of the string literal in the global array) to the reference variable so that the original can be accessed (read-only) with r[] etc?
and g(f(1)); pass a pointer to g() where the string s is then initialized with the pointer?
What am I missing? Do const char* always produce a temporary string when it's returned from a function?
There is a implicit conversion, the code
string s = f(2);
is equal to
string s = string(f(2));
I don't quite get why temporary strings are made according to the comments.
The whole matter is that a char const* and a std::string are totally different data types.
For comparison:
class A { };
class B { };
void f(A a);
void g()
{
B b;
f(b); // does not work...
}
I'm pretty sure you have encountered that already.
Now let's change class A:
class A
{
public:
A() { } // default constructor
A(B b) { } // accepting an instance of B
};
Now, you can do:
B b;
f(A(b)); // create a TEMPORARY A from B; you need it, as f ONLY accepts an A!
f(b); // again, create a temporary just as before - this time IMPLICITLY
You can disallow implicitly creating A from B by making the constructor explicit:
class A
{
public:
A() { }
explicit A(B b) { } // now A cannot be created implicitly any more
};
B b;
//f(b); // now is not accepted by compiler any more
f(A(b)); // this still works, of course
Exactly the same with std::string: It has a non-explicit constructor accepting char const*...

removing the const in copy constructor

This is what I did originally.
class A
{ public:
A() { std::cout << "\ndefault constructor"; }
A(const A&) { std::cout << "\ncopy constructor"; }
A(int) { std::cout << "\nconversion constructor"; }
};
A a0; // print default constructor
A a1(a0); // print copy constructor note : direct initialization
A a2 = a0; // print copy constructor note : copy initialization
A a3(123); // print conversion constructor note : direct initialization
A a4 = 123; // print conversion constructor note : copy initialization (create a temp object from int)
However, if class A is slightly modified as the following (remove const in copy constructor), why are there compile error for the final line? thank you
class A
{ public:
A() { std::cout << "\ndefault constructor"; }
A(A&) { std::cout << "\ncopy constructor"; }
A(int) { std::cout << "\nconversion constructor"; }
};
A a0; // print default constructor
A a1(a0); // print copy constructor note : direct initialization
A a2 = a0; // print copy constructor note : copy initialization
A a3(123); // print conversion constructor note : direct initialization
//A a4 = 123; // compile error
A a4 = 123;
is equivalent to
A a4 = A(123); // The RHS is a temporary A object.
That works for the first case since there is a constructor that takes a A const& as the argument type.
That does not work if the argument type is A&. A temporary object can be used when the argument type is A const&, not when it is A&.
For the case A a4 = 123;, when object “a4” is being constructed, the statement
A a4 = 123;
is broken down by the compiler as
A a4 = A(123);
In above statement, one argument constructor i.e. A(int) is used to convert integer value “123” to a temporary object & that temporary object is copied to the object “a4” using copy constructor. C++ does not allow to pass temporary objects by non-const reference because temporary objects are rvalue that can't be bound to reference to non-const.
So, generically, If you're not passing your argument with a const qualifier, then you can't create copies of const objects.
One more similar example for better understanding:
class Test
{
/* Class data members */
public:
Test(Test &t) { /* Copy data members from t*/}
Test() { /* Initialize data members */ }
};
Test fun()
{
cout << "fun() Called\n";
Test t;
return t;
}
int main()
{
Test t1;
Test t2 = fun(); //compilation error with non-const copy constructor
return 0;
}
$g++ -o main *.cpp
main.cpp: In function ‘int main()’:
main.cpp:22:18: error: cannot bind non-const lvalue reference of type ‘Test&’ to an rvalue of type ‘Test’
Test t2 = fun();
~~~^~
main.cpp:8:4: note: initializing argument 1 of ‘Test::Test(Test&)’
Test(Test &t) { /* Copy data members from t*/}
^~~~

Why is the 'explicit' keyword allowing implicit conversions?

class Test {
private:
int value;
public:
void display(void)
{
cout << "Value [" << value << "]" << endl;
}
explicit Test(int i)
{
value=i;
}
};
int main() {
Test a(5);
Test b(4.9);
a.display();
b.display();
cin.get();
return 0;
}
Float value gets converted to int even though explicit is mentioned.
I was expecting (incorrectly) that float does not get converted to integer and object b not to be constructed.
explicit refers to the constructor itself, not the constructor's parameters. Your explicit constructor may not be used as an implicit conversion to type Test.
void function( Test param );
function( 5 ); // Your "explicit" makes this call an error.
// The parameter must be explicitly cast, such as Test(5)
In C++11 or later, you can prevent implicit parameter conversions using the = delete syntax on a template parameter.
Test(int i)
{
value=i;
}
template<typename T>
Test(const T&) = delete;
// ^ Aside from your int constructor and the implicitly-generated
// copy and move constructors, this will be a better match for any other type
In C++20 or later, you can prevent implicit parameter conversions using the std::same_as concept.
Test(std::same_as<int> auto i)
{
value=i;
}
explicit just prevents any implicit conversions. So if you had:
void foo(Test t);
You cannot call foo(4); because the Test constructor is explicit. You'd have to call foo(Test(4));. The explicit keyword has nothing to do with any conversions that might have to happen during construction.
From the standard [class.conv.ctor]:
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the
direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used.
Which means that Test t = 4; is also illegal, but Test t(42.0) is fine.
It's a Floating–integral conversion.
That is: it's an implicit conversion between a prvalue of type double to a prvalue of type signed int. It discards the fractional part.
TL;DR: the conversion happens between 'double' and 'int', not in your Test constructor. If you want to prevent that constructor to be called with a float or a double you can add the definition:
Test(double) = delete;
In your Test class. Live on compiler explorer

Constructor overloading chooses cast operator instead of struct type

I've encountered a weird situation where the compiler chooses to cast a structure even though there's a perfectly good constructor that receives the structure type.
A small example:
struct A
{
operator int() {return 1;}
};
struct B
{
B(A& a) { OutputDebugStringA("A constructor\n"); }
B(int i) { OutputDebugStringA("int constructor\n"); }
};
A test () { A a; return a;};
int _tmain(int argc, _TCHAR* argv[])
{
B b(test());
return 0;
}
Explanation: A has a cast operator to int. B has 2 overloaded constructors, one that accepts A reference, and one that accepts int.
Function test() returns an A object.
For some reason, the compiler decides to cast the return value to an int, and use the constructor that accepts an int. int constructor
Can anyone explain why this happens ? I have a few theories, but I would like an answer that is based on something real (maybe a quote from the standard).
Note:
I can get the expected result (constructor that accepts the type) by changing the constructor signature to: B(const A& a) or B(A&& a)
Your constructor takes a non-const reference to A, which cannot bind to a temporary. You pass it a temporary here:
B b(test());
// ^^^^^^ temporary A
so the only valid constructor is the one taking an int. You can change this behaviour by making the relevant constructor take a const reference:
B(const A& a) { OutputDebugStringA("A constructor\n"); }
//^^^^^
and similarly for B(A&& a); in C++11.
Alternatively, keeping the original constructor signature and passing an lvalue also results in the constructor call:
A a;
B b(a);

Why the first copy constructor is called in the code below ?

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 ;)