Deleted copy constructor of tuple member cause error - c++

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

Related

Copy constructor implicit conversion problem

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.

Why is this deleted move constructor accepted by the compiler?

In the following code example, I have no particular reason to delete the move constructor, but I don't understand why it compiles (x is an object that prints whether it is moved or copied).
class foo {
X x;
public:
foo()=default;
foo(const foo &)=default;
foo(foo &&)=delete;
};
int main()
{
vector<foo> v;
foo a;
v.push_back(a);
v.push_back(a);
}
The second time push_back is called on the std::vector, it relocates the already existing object, usually with a move operation. With the foo move constructor declared 'default', I can see this happen.
However, when the move constructor is explicitly deleted, I'd expect compilation to fail since it is still available for overload, but deleted.
It does compile and the vector reallocation uses the copy constructor.
What is happening here ?
std::vector::resize() will move if it can, and copy if it must. This is extremely useful, as not all copyable types are movable. Keep in mind in c++ that objects are value types, not reference types.
I think you're using the copy constructor here aren't you? foo(const foo &). Keep in mind in c++ that objects are value types, not reference types.
To move a into the vector you have to make it a r-value, that is, to use std::move(a) instead of just a.
I believe if you do that your code will error on the deleted move ctor.

Copying parameter invokes deleted constructor when that constructor shouldn't be called

#include <memory>
template <typename T>
class Wrapper {
public:
Wrapper() = delete;
Wrapper(const Wrapper&) = delete;
Wrapper(Wrapper&&) = delete;
~Wrapper() = default;
Wrapper(const T&) = delete;
Wrapper(T&& in) : instance{std::move(in)} {}
T instance;
};
void foo(Wrapper<std::shared_ptr<int>>) {}
int main() {
auto ptr = std::make_shared<int>(1);
foo(std::move(ptr));
}
This has been working in C++17 so I never gave it thought but why does this code try and invoke the move constructor in C++14? Shouldn't it be constructed in place in the function argument? This seems to not be a problem with c++17 but is not compiling with c++14.
The only workaround I see is to make the foo parameter an rvalue, but is there anything I can do to make this work without making the parameter in foo an rvalue in C++14?
My first thought would be that a temporary would have to be constructor in order to be passed to the function but what is even more surprising is that even with -fno-elide-constructors and undeleting the move constructors and copy constructors those do not seem to be called! Is this a bug in gcc and clang both?
See https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 for the error
And see for the strange behavior https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz
When you call foo(std::move(ptr)); you are not giving it a Wrapper<std::shared_ptr<int>>. So, the compiler generates a temporary and uses that to construct foo's parameter. Now, this can be elided out and we can directly construct a Wrapper<std::shared_ptr<int>> but the move/copy constructor still needs to be accessible, even if it is never called.
With C++17 this no longer happens. We have guaranteed copy elision which means no temporary is ever materialized and instead the parameter is directly constructed.

About constructors and assign operators in C++

I simply created a class like this:
class GreatClass
{
public:
GreatClass(){cout<<"Default Constructor Called!\n";}
GreatClass(GreatClass &gc){cout<<"Copy Constructor Called!\n";}
GreatClass(const GreatClass &gc){cout<<"Copy Constructor (CONST) Called!\n";}
~GreatClass(){cout<<"Destructor Called.\n";}
GreatClass& operator=(GreatClass& gc){cout<<"Assign Operator Called!";return gc;}
const GreatClass& operator=(const GreatClass& gc){cout<<"Assign Operator (CONST) Called!";return gc;}
};
GreatClass f(GreatClass gc)
{
return gc;
}
and in main() function, there are two versions:
version #1:
int main()
{
GreatClass g1;
GreatClass G = f(g1);
}
version #2:
int main()
{
GreatClass g1;
f(g1);
}
They all generates the SAME output:
Default Constructor Called!
Copy Constructor Called!
Copy Constructor Called!
Destructor Called.
Destructor Called.
Destructor Called.
I do not understand why there is nothing happening when I'm assigning f(g1) to G. What constructor or operator is called at this point?
Thanks.
Compiler implementations are allowed to elide/remove copy constructor calls in certain cases, the example you specify is a good example use case of such a scenario. Instead of creating a temporary object and then copying it to destination object the object is created directly in the destination object and the copy constructor call is removed out.
This optimization is known as Copy elision through Return value optimization.
Also, with C++11 move semantics through rvalue references might kick in instead of the Copy semantics. Even with move semantics the compilers are still free to apply RVO.

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.