Copy constructor implicit conversion problem - c++

Part of the answer is given in here
class foo1
{
private:
int i;
public:
foo1()
{
i=2;
}
int geti(){
return i;
}
};
class foo2
{
private:
int j;
public:
explicit foo2(foo1& obj1) //This ctor doesn't have `const` intentionally as i wanted to understand the behavior.
{
cout<<"\nctor of foo2 invoked";
j=obj1.geti();
}
};
foo1 obj1;
foo2 obj2(obj1); //THIS WORKS
foo2 obj22=obj1; //THIS DOESN'T WORK
Both of them work when the keywork explicit is removed.
Is the following explanation correct for copy initialization( foo2 obj22=obj1) :
At first the compiler creates a temporary object using constructor:
foo2(foo1& other) {}
then it tries to use this temporary object in the copy constructor:
foo2(foo2& other) {}
But it may not bind a temporary object with non-constant reference and it issues an error.
When you are using the equal sign then there is used so-called copy-initialization.
If yes, then shouldn't foo2(foo1& other) {} itself be disallowed before even going further because temporary cannot be bound to non const reference?
Sorry for the long question, my confusion is basically around difference behavior for direct and copy initialization in presence/absence of explicit keyword(with/without const)

Is the following explanation correct for copy initialization
It isn't. You omitted the definition of a copy c'tor for foo2. Which means the compiler synthesizes one for you automatically. The synthesized version will take by a const foo2&. So the issue is not in binding a temporary foo2 to a non-const reference.
As a matter of fact, copy initialization no longer creates temporaries, and doesn't even have to behave as though a temporary is present. What instead happens is that the form of initialization simply takes the explicit keyword into account.
Both of these
foo2 obj2(obj1);
foo2 obj22=obj1;
perform the same overload resolution, which can only choose the same c'tor (foo2(foo1& obj1)). It should choose this c'tor because you pass a non-const lvalue for an argument.
The difference? Direct initialization is allowed to use an explicit c'tor. While copy initialization does not. So your explicit c'tor is not a candidate, and overload resolution fails.

Related

Error spotted in C++ Primer 5th Edition (copy intialization vs direct initialization)

Hi i am trying to understand how copy constructor works and looking at an example. The example is as follows:
{//new scope
Sales_data *p = new Sales_data;
auto p2 = make_shared<Saled_data>();
Sales_data item(*p); // copy constructor copies *p into item
vector<Sales_data> vec;
vec.push_back(*p2);// copies the object to which p2 points
delete p;
}
My question is :
Why it is written that "copy constructor copies *p into item"? I mean, item is direct initialized. If we would have written Sales_data item = *p; then it will be called copy initialized, so why they have written copy constructor copies *p into item in the comment.
Now, to verify this for myself, i tried creating a simple example myself, but there also i am unable to understand the concept properly. My custom example is as follows:
#include<iostream>
#include<string>
class MAINCLASS{
private:
std::string name;
int age =0;
public:
MAINCLASS(){
std::cout<<"This is default initialization"<<std::endl;
}
MAINCLASS(MAINCLASS &obj){
std::cout<<"This is direct initialization"<<std::endl;
}
MAINCLASS(const MAINCLASS &obj):name(obj.name),age(obj.age){
std::cout<<"This is copy initialization"<<std::endl;
}
};
int main(){
MAINCLASS objectone;
MAINCLASS objecttwo =objectone;
MAINCLASS objectthree(objectone);
return 0;
}
Now when i run this program, i get the following output:
This is defalut initialization
This is direct initialization
This is direct initialization
My question from this program is as follws:
Why are we not getting the output "this is copy initialization" in the second case when i write MAINCLASS objecttwo =objectone;? I have read that in direct initialization function matching is used and in copy constructor , we copy the right hand operand members into left hand operand members. So when i write MAINCLASS objecttwo =objectone; it should call the copy constructor and print "this is copy initialization" on the screen. But instead it is direct initializing the object. What is happening here?
Despite the poor choice of name, copy initialization is orthogonal to copy constructors.
A copy constructor is any constructor whose first parameter is a lvalue reference to its class type, and can be called with just one argument. It's just a constructor that can initialize new objects from existing objects. That's pretty much all there is to it. Both the constructors you declared are in fact copy constructors. This one would be too
MAINCLASS(MAINCLASS volatile &obj, void *cookie = nullptr) {
// .. Do something
// This is a copy c'tor since this is valid:
// MAINCLASS volatile vo;
// MAINCLASS copy1_vo(vo);
}
And as the other answers noted copy initialization is simply the name for a family of initialization contexts. It includes initialization involving =, passing arguments to functions, return statements and throw expressions (and I'm probably forgetting something). Direct initialization involves other contexts.
A copy constructor can be used in any of the above. Be it copy initialization or direct initialization. The difference between the two - as appertains to constructors - is how an overload set of constructors is built. Copy initialization doesn't make use of constructors declared explicit. For instance, in this example
struct Example {
Example() = default;
explicit Example(Example const&) {}
};
int main() {
Example e;
Example e1(e); // Okay, direct initialization
Example e2 = e1; // Error! Copy initialization doesn't make use of explicit constructor
}
Even though we have a copy constructor, it can't be called in a copy-initialization context!
As far as the unexpected print out of your program, it's simply a matter of overload resolution choosing a more matching function. Your origin object is not declared const. So binding it to a non-const lvalue reference is simply the preferred choice in overload resolution.
Don't confuse copy construction and copy initialisation. You can copy-construct using direct or copy initialisation.
Copy initialisation refers to a set of initialisation syntax and semantics. This includes the T a = b syntax.
The copy constructor is a special class method that takes an argument of said class. This method should only take one parameter (both T& or const T& will do). Copy construction occurs when that function is called.
With this in mind, we can go on to answer your questions.
Why it is written that "copy constructor copies *p into item"? I mean, item is direct initialized. If we would have written Sales_data item = *p; then it will be called copy initialized...
Both Sales_data item = *p and Sales_data item(*p) call the copy constructor. But, the former uses copy initialisation (T a = b), whereas the latter uses direct initialisation (T a(b)).
Why are we not getting the output "this is copy initialization" in the second case when i write MAINCLASS objecttwo =objectone;?
Actually, the issue here isn't whether it's copy/direct initialised. This is an issue of lvalue/rvalue overload resolution.
Consider the following program:
#include <iostream>
void f(int& i) { std::cout << "int&\n"; }
void f(const int& i) { std::cout << "const int&\n"; }
int main() {
f(1); // f(const int&)
int i = 2;
f(i); // f(int&)
}
f is chosen based on whether the value passed is lvalue or rvalue. In the first case, 1 is an rvalue, so f(const int&) is called (see this). In the second case, i is an lvalue, and f(int&) is chosen since it's more general.
So in your case, both MAINCLASS objecttwo =objectone; and MAINCLASS objectthree(objectone); call the copy constructor. And again, the former uses copy initialisation, whereas the latter uses direct initialisation. It's just that both of these calls choose the non-const ref overload instead: MAINCLASS(MAINCLASS&).
Copy initialization and direct initialization is based on the syntax used to construct.
See Confusion in copy initialization and direct initialization.
Which constructor gets invoked is based on overload resolution (and not the syntax to construct)
The compiler invokes the function which best matches the passed arguments to the defined parameters.
In your example since objectone is non-const, the best match is the copy constructor with a non-const parameter. Since the other copy constructor has a const& parameter, it will get invoked for a const object.
Rewriting your example:
#include<iostream>
#include<string>
class MAINCLASS {
private:
std::string name;
int age = 0;
public:
MAINCLASS() {
std::cout << "This is default initialization" << std::endl;
}
MAINCLASS(MAINCLASS& obj) {
std::cout << "This is copy constructor with non-const reference parameter" << std::endl;
}
MAINCLASS(const MAINCLASS& obj) :name(obj.name), age(obj.age) {
std::cout << "This is copy constructor with const reference parameter" << std::endl;
}
};
int main() {
MAINCLASS objectone;
const MAINCLASS const_objectone;
MAINCLASS objecttwo = objectone; // copy initialization of non-const object
MAINCLASS objectthree(objectone); // direct initialization of non-const object
MAINCLASS objectfour = const_objectone; // copy initialization of const object
MAINCLASS objectfive(const_objectone); // direct initialization of const object
return 0;
}
The output would be:
This is default initialization
This is default initialization
This is copy constructor with non-const reference parameter
This is copy constructor with non-const reference parameter
This is copy constructor with const reference parameter
This is copy constructor with const reference parameter

Deleted copy constructor of tuple member cause error

This piece of code
#include <tuple>
struct Foo
{
Foo(const int& value):value_(value){}
//Foo(const Foo&)=delete; // delete copy constructor
int value_;
};
int main()
{
std::tuple<Foo> tup(std::move(Foo(1)));
return 0;
}
works fine but if you delete the Foo copy constructor it fails with the following compile error: use of deleted function Foo::Foo(const Foo&).
But, since I am telling explicitly that the object can be moved, why the std::tuple constructor uses the Foo copy constructor instead of its moving constructor? How can I enforce the std::tuple to be constructed moving the Foo instance instead of copying it?
But, since I am telling explicitly that the object can be moved,
No, you are telling the compiler that you want the object to be moved. That's not the same thing.
why the std::tuple constructor uses the Foo copy constructor instead of its moving constructor?
Because when you delete the copy constructor the implicit move constructor does not exist, so it can't be used.
How can I enforce the std::tuple to be constructed moving the Foo instance instead of copying it?
Either don't delete the copy constructor, or define a move constructor as well:
struct Foo
{
Foo(const int& value):value_(value){}
Foo(const Foo&)=delete; // delete copy constructor
Foo(Foo&&)=default;
int value_;
};
N.B. the std::move here is completely useless:
std::tuple<Foo> tup(std::move(Foo(1)));
All std::move does is cast its argument to an rvalue, but the temporary Foo(1) is already an rvalue, so you are casting an rvalue to an rvalue: useless. Furthermore, without the std::move the compiler can do copy elision and optimise away the actual move, but when you use std::move it can't do that and you make the code slower not faster!
The optimum code is the simplest version:
std::tuple<Foo> tup(Foo(1));
Or even simpler:
std::tuple<Foo> tup(1);

Why copy elision not working with std::move?

I use the code below to test copy elision:
class foo
{
public:
foo() {cout<<"ctor"<<endl;};
foo(const foo &rhs) {cout<<"copy ctor"<<endl;}
};
int g(foo a)
{
return 0;
}
int main()
{
foo a;
g(std::move(a));
return 0;
}
I expected only the default constructor would be called because the argument of g() is an rvalue and copy will be elided. But the result shows that both the default constructor and the copy constructor are called. Why?
And if I change the function call to g(foo()), the copy will be elided. What's the difference between the return types of foo() and std::move(a)? How can I make the compiler elide copy on an lvalue?
Copy elision for can only occur in a few specific situations, the most common of which is the copying of a temporary (the others are returning locals, and throwing/catching exceptions). There is no temporary being produced by your code, so no copy is elided.
The copy constructor is being called because foo does not have a move constructor (move constructors are not implicitly generated for classes with explicit copy constructors), and so std::move(a) matches the foo(const foo &rhs) constructor (which is used to construct the function argument).
A copy of an lvalue can be elided in the following situations (although there is no way to force a compiler to perform the elision):
foo fn() {
foo localAutomaticVariable;
return localAutomaticVariable; //Copy to construct return value may be elided
}
int main() {
try {
foo localVariable;
throw localVariable; //The copy to construct the exception may be elided
}
catch(...) {}
}
If you want to avoid copies when passing function arguments, you can use a move constructor which pilfers the resources of the objects given to it:
class bar {
public:
bar() {cout<<"ctor"<<endl;};
bar(const bar &rhs) {cout<<"copy ctor"<<endl;}
bar(bar &&rhs) {cout<<"move ctor"<<endl;}
};
void fn(bar a)
{
}
//Prints:
//"ctor"
//"move ctor"
int main()
{
bar b;
f(std::move(b));
}
Also, whenever copy elision is allowed but does not occur, the move constructor will be used if it is available.
You need to declare g as:
int g(foo && a) //accept argument as rvalue reference
{
return 0;
}
Now it can accept argument by rvalue-reference.
In your case, even though the expression std::move(a) produces rvalue, it doesn't bind to a parameter which accepts argument by value. The receiving end must be rvalue-reference as well.
In case of g(foo()), the copy-elision is performed by the compiler, which is an optimization. It is NOT a requirement by the language[until C++17]. You can disable this optimization if you want to : then g(foo()) and g(std::move(a)) will behave exactly same, as expected.
But if you change g as I suggested above, the call g(foo()) will not make a copy because it is a requirement by the language to not make copy with &&. It is not a compiler-optimization anymore.

copy constructor by address

I have two copy constructors
Foo(Foo &obj){
}
Foo(Foo *obj){
}
When will the second copy constructor will get called?
No you don't - you have one copy constructor
Foo( Foo * obj );
is not a copy constructor, and will never be used as such by the C++ compiler. You can of course use it yourself:
Foo a;
Foo b( & a ); // use your constructor
Note also that your real copy constructor should be declared as;
Foo( const Foo & f );
though the lack of const does not prevent it from being a copy constructor.
Leaving aside that the second constructor isn't a copy constructor - you actually wanted to know when the second constructor will be called.
The Foo(Foo* obj); constructor is a single parameter constructor - because it hasn't been marked with the explicit keyword, it provides an implicit conversion from Foo* to Foo. It may be called at any time where a Foo* is used in the place of a Foo or const Foo& - if it's being called unexpectedly, that's almost certainly what's happening.
In general, single parameter constructors should either be copy constructors (which other answers have explained) or should be marked explicit. Constructors which provide implicit conversions should be used sparingly.
The second is not a copy constructor. It is a constructor that gets called when you create a new Foo object, giving a pointer to a Foo as parameter.
Foo foo0;
Foo foo1 = foo0; // Calls copy constructor
Foo foo2(foo0); // Calls copy constructor
Foo foo3(&foo0); // Calls constructor taking a pointer as parameter
One of your constructors is a copy constructor, the other is just a normal constructor.
The second will be called if you explicitly initialize a Foo from a pointer to Foo or in other situations where an conversion from a pointer to Foo to an r-value Foo is called for, such as argument passing and function returns.
It's usually a bad idea to have such an implicit conversion; it may occur when you don't expect it to and is liable to turn trivial typos from compile errors into unusual behaviour at runtime.

One question about array without default constructor in C++

From previous post, I learnt that for there are two ways, at least, to declare an array without default constructors. Like this
class Foo{
public:
Foo(int i) {}
};
Foo f[5] = {1,2,3,4,5};
Foo f[5] = {Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)};
I also learnt that the first one will construct the object using the parameter directly and the second copy constructor is used here. However, when I test the code below. I make the copy constructor private. I expect to see the difference of the copy constructor usage. But it is not what I expected. Neither of the two declarations is working.
class Foo{
public:
Foo(int i) {}
private:
Foo(const Foo& f) {}
};
int main(){
Foo f[5] = {1,2,3,4,5};
Foo f[5] = {Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)};
}
Can anybody explain to me why does this happen?
The first won't construct the objects directly. It will first construct a temporary Foo, and then copy the Foo into the element. It's similar to your second way. The difference is that your second way won't work with a explicit copy constructor, while your first will. And conversely, the first will not work with a explicit constructor taking int, while the second will. Stated another way, the first constructor used in the initialization of an element must not be explicit.
Notice that neither way needs to copy. But they still need to check whether the copy constructors are accessible. So, they shall behave as-if they would copy, but they don't really need to do the copy.