I have a class where the move assignment is explicit deleted, since the object should not be moveable. But if i assign to an instance of this class using RVO the compiler gives me the error:
main.cpp:12:16: note: candidate function has been explicitly deleted
also the compiler is mentioning the existing copy assignment operator but does not use it.
here is my code (or a (not) running example here):
class foo {
public:
foo() {}
foo(foo const& r) {}
foo(foo&&) = delete;
foo const& operator=(foo const& r) { return *this; }
foo const& operator=(foo&& r) = delete;
};
int main(int argc, char **argv) {
foo bar;
bar = foo();
return 0;
}
I found a quite similar post here.
I know I can avoid this by using a temporary. i wonder why every compiler (i tested this with gcc, clang and vs2013) is not able to call the existing copy assignment directly? Is there something I am missing?
The copy assignment is not called, because the (deleted) move assignment is a better match for overload resolution.
Simply don't declare the move assignment at all. Then the copy assignment will be selected. The implicit move assignment operator won't be generated because the class has a user declared copy constructor, move constructor and copy assignment operator. Any of those will prevent the generation of the implicit move assignment operator.
But if i assign to an instance of this class using RVO
There is no RVO involved here. You create a temporary foo and copy assign it to an existing variable. Copy assignments cannot be elided.
Also, it's quite unusual and inefficient to return by value from an assignment operator.
Just like std::move() applies a static_cast<>() to force the use of the move assignment/constructor, one can do something similar to force the use of the copy assignment/constructor:
#include <iostream>
class foo
{
public:
foo() {}
foo(foo const& r) {}
foo(foo&&) = delete;
foo const& operator=(foo const& r) { std::cout << ":)\n"; return *this; }
foo const& operator=(foo&& r) = delete;
};
int main(int argc, char **argv)
{
foo bar;
bar = static_cast<const foo&>(foo());
return 0;
}
You could use placement new to do this:
#include <iostream>
#include <string>
#include <new>
class foo {
public:
foo() {}
foo(foo const& r) {}
foo(foo&&) = delete;
foo const& operator=(foo const& r) { return *this; }
foo const& operator=(foo&& r) = delete;
};
int main(int argc,char **argv)
{
foo bar;
//bar = foo();
bar.~foo();
new(&bar) foo();
return 0;
}
Related
I'm struggling to understand the exact reason the program fails.
Regarding the following program:
#include <iostream>
template < class T, size_t SIZE>
class Stack {
T arr[SIZE] = {};
int pos = 0;
public:
Stack & push(const T & t) {
arr[pos++] = t;
return *this;
}
Stack & push(T && t) {
arr[pos++] = std::move(t);
return *this;
}
T pop() {
return std::move(arr[--pos]);
}
};
class Foo {
std::string s;
public:
Foo(const char* s = "#") : s(s) {}
Foo(Foo && foo) : s(std::move(foo.s)) {}
Foo & operator=(Foo && foo) {
s = std::move(foo.s);
return *this;
}
void print() const { std::cout << s << std::endl; }
};
int main() {
Stack<std::string, 5> s1;
s1.push("hi").push(std::string{ "bye" });
std::cout << s1.pop() << std::endl;
Stack<Foo, 5> s3;
Foo f2;
s3.push(f2);
}
The program fails at s3.push(f2); is it because Foo doesn't have a copy ctor or is it because its assignment operator function only handles Foo&& types?
I'm suspecting it's the assignment operator function but I'm not sure it's not because of copy ctor as well.
Because you provided a custom move cosntructor and assignment operator, the compiler no longer generates default copy constructor and assignment operator.
You either need to write those too, or, even better, remove the custom move operations.
The compiler will then generate all 4 for you, and they might be better than your manually written ones (e.g. one problem is that you forgot noexcept on move operations, so standard containers will prefer making copies to moves in some scenarios).
Well, the compiler tells you:
error: use of deleted function ‘Foo& Foo::operator=(const Foo&)’
So you can solve it by adding that function:
Foo& operator=(const Foo& foo) {
s = foo.s;
return *this;
}
There are 2 solutions (I will not point out design issues assuming you are just experimenting).
As a internal data use vector<T> instead of the array of fixed size and in the push that accepts the const& move and use emplace_back (nasty and amoral, also it will leave the src in invalid state).
Provide copy constructor to Foo (this is much more easier)
This is in c++, using Visual Studio 2019 (haven't tried other compilers).
I want to add a templated operator= method. If the parameter is non-const, it works fine. But if the parameter is const, even if I make a version with a const parameter, it isn't called. Instead, it does a simple shallow copy.
If I use a named function instead of the operator, it works as expected. Similarly, if it's not templated, the operator is called as expected. The combo seems to be the issue.
Here's an example that exhibits the issue.
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
// non-const operator=
template<class SrcType>void operator=(SrcType& src)
{
x = src.x;
}
// const operator=
template<class SrcType>void operator=(const SrcType& src)
{
x = src.x;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // ? shallow copy, not calling const operator=
return 0;
}
Any ideas how to get it to use my overloaded function? Thanks.
Your const function template is not called because compiler generated default copy assignment operator by default, which has signature operator=(const CTest&). When compiler must choose between non-template function and template one (when both has the same match), the former is preferred. That is why your templated method is not called.
To help compiler to select version you want, add:
CTest& operator=(const volatile CTest&) = delete;
by above, you disable normal operator=, volatile here is imporant, without it compiler will complain that operator= is disabled. By adding volatile, you just make that template version is better matched than volatile one.
The rest is not changed:
template<class SrcType>
void operator=(SrcType& src)
{
x = src.x;
puts("boom1");
}
template<class SrcType>
void operator=(const SrcType& src) {
x = src.x;
puts("boom2");
}
Demo
If a class does not have a copy-assignment declared, the compiler will generate one implicitly.
You class does not have a copy-assignment operator, it only has a template assignment operator.
When called with a const object the implicitly declared assignment operator is a better match during overload resolution.
The issue here is that we can't delete the implicitly declared assignment operator, since it will then generate a compiler error. We can however write our own assignment operator that forwards to our template.
#include <iostream>
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
CTest(const CTest& v) = delete;
CTest& operator=(const CTest& v) {
return operator=<CTest>(v);
}
// non-const operator=
template<class SrcType>CTest& operator=(SrcType& src)
{
x = src.x;
std::cout << "non-const\n";
return *this;
}
template<class SrcType>CTest& operator=(const SrcType& src)
{
x = src.x;
std::cout << "const\n";
return *this;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // calls copy-assignment and forwards to template
return 0;
}
The following code will crash under Visual Studio 2013
I'm wondering why : what's the correct way to write a move constructor in such a case ?
Removing the move constructor solves the problem.
Is it a bug of VC++ or is this code wrong ?
What would be different in the default definition of the move constructor that makes this code not crash whereas my own definition does ?
#include <memory>
#include <vector>
class A
{};
class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}
Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }
protected:
std::unique_ptr<A> mRef;
};
int main(int argc, char *argv[])
{
std::vector<Foo>({ Foo(std::make_unique<A>()), Foo(std::make_unique<A>()) });
// Crash : Debug Assertion Failed !
// Expression : _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
}
It might be somehow related to this, right ?
Double delete in initializer_list vs 2013
Here is the actual code with fleshed out copy constructor and assignment, but the bug is exactly the same
class A
{
public:
std::unique_ptr<A> clone() { return std::make_unique<A>(*this); }
};
class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}
Foo(const Foo& other) : mRef(other.mRef->clone()) {}
Foo& operator=(const Foo& other) { mRef = other.mRef->clone(); return *this; }
protected:
std::unique_ptr<A> mRef;
};
This sounds like a VS-2013 bug. But it also looks like your code, though well-formed, is probably not doing what you want it too (however only you can say that for sure).
I added a print statement to your Foo:
class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}
Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }
friend std::ostream&
operator<<(std::ostream& os, const Foo& f)
{
if (f.mRef)
os << *f.mRef;
else
os << "nullptr";
return os;
}
protected:
std::unique_ptr<A> mRef;
};
And I added a print statement to your main as well:
aside: I also added identity/state to your A so that it was easier to see what is going on.
int main(int argc, char *argv[])
{
std::vector<Foo> v({ Foo(std::make_unique<A>(1)), Foo(std::make_unique<A>(2)) });
// Crash : Debug Assertion Failed !
// Expression : _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
for (const auto& p : v)
std::cout << p << '\n';
}
For me this outputs:
nullptr
nullptr
which I believe is the correct output.
One can not move from an initializer_list, and thus the vector constructor invokes Foo's copy constructor which just default constructs unique_ptr.
Indeed if one removes the Foo copy constructor, which should be implicitly deleted then (or you could explicitly delete it), the program should not compile.
To really make this work, you have to give Foo an operational copy constructor. Perhaps something like this:
Foo(const Foo& other)
: mRef(other.mRef ? new A(*other.mRef) : nullptr)
{}
So in summary, I think both the compiler and the current code get awards for bugs. Though from the comments, it sounds like the current code bug is simply an artifact of correctly reducing the code to isolate the problem.
VS-2013 bug.
As for your move constructor, it is fine. Though it would be even better if implemented with = default. If you do so, it will automatically inherit a noexcept spec. Such a spec should not be taken lightly. It is most important for efficient use of vector<Foo>.
My understanding is that VS-2013 does not understand either defaulted move members nor noexcept.
My anecdotal experience with VS-2013 is that the number of compiler bugs one experiences is directly proportional to the use of curly braces. I am hopeful that VS-2015 will contradict this experience. In the meantime I recommend avoiding using construction expressions that involve {}.
Update
Your updated copy members in:
class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}
Foo(const Foo& other) : mRef(other.mRef->clone()) {}
Foo& operator=(const Foo& other) { mRef = other.mRef->clone(); return *this; }
protected:
std::unique_ptr<A> mRef;
};
have potential nullptr-dereference bugs. If other is in a moved-from state, the ->clone() is going to dereference a nullptr. Technically you can get away with this if you are very, very careful. However it will be very easy for you to accidentally hit this bug.
I've got a std::vector<Foo> where Foo is a class containing Foo( Foo&& ) noexcept.
Adding objects to the container works flawlessly, however erasing them using std::vector::erase( iterator ) does not, GCC 4.7 tries to call the assignment operator which I have deleted. The exact error message is:
error: use of deleted function ‘Foobar& Foobar::operator=(const Foobar&)
Edit: Of course std::vector calls the assignment operator, not the copy constructor (you can see that in the error message, too). Fixed it in the description, sorry.
Here's example source code as requested:
#include <vector>
class Foo {
public:
Foo() {}
Foo( Foo&& other ) noexcept {}
Foo( const Foo& ) = delete;
Foo& operator=( const Foo& ) = delete;
};
int main() {
std::vector<Foo> v;
v.push_back( Foo{} );
v.erase( v.begin() );
}
The problem is that you did not provide a move assignment operator. This is part of the vector Movable requirements for some functions.
I couldn't reproduce it. Turns out good habits go a long way: I had the move asignment operator defined.
Live on GCC 4.7.2: http://liveworkspace.org/code/36c600c285f2c91649fd4f73784c2c00
#include <iostream>
#include <vector>
struct Foo
{
Foo() {}
Foo(Foo const&) = delete;
Foo(Foo&&) throw() { }
Foo& operator=(Foo const&) = delete;
Foo& operator=(Foo&&) throw() { return *this; }
};
int main(int argc, char* args[])
{
std::vector<Foo> v;
v.emplace_back();
v.emplace_back();
v.emplace_back();
v.emplace_back();
auto it = v.begin();
it++;
v.erase(it);
}
DeadMG's answer is excellent, however I'd like to promote another way of writing the assignment operator:
struct Foo {
Foo() {}
Foo(Foo const&) = delete;
Foo(Foo&&) throw() { }
Foo& operator=(Foo) throw() { return *this; }
};
Since you require a fresh temporary at the start of the method, the compiler will pick either the copy or move constructor to create this temporary on its own, and you do not have to write both a copy assignment operator AND a move assignment operator :)
It seems that initalizer lists are a good idea for your class constructors and, I'm assuming, for the copy constructor as well. For the assignment operator one has to assign each member in the body of the function. Consider the following simple block:
class Foo {
private:
int a,b;
public:
Foo(int c, int d) : a(c), b(d) {}
Foo(const Foo & X) : a(X.a), b(X.b) {}
Foo& operator=(const Foo& X) {
if (this == &X) return *this;
a = X.a;
b = X.b;
return *this;
}
};
If a class has a moderate amount of data members, there are three places where one can mess up the the different assignments/initialization. By that I mean, what if the copy constructor looked like:
Foo(const Foo & X) : a(X.a), b(X.a) {}
or a line was missing from the operator=. Since the assignment operator and the copy constructor often have the same effect (in that we copy members from one Foo to another) can I "reuse" the code from the copy constructor or the assignment operator or vice versa?
Your goal should be to not write copy constructors/assignment operators at all. Your goal should be to let the compiler do it. Standard library containers are all copyable, so use them where reasonable.
If there are members that cannot be copied correctly, then use smart pointers or other RAII objects. Those objects are the ones that should need special copy constructors/assignments. And they only need them for their one member.
Everything else should not use them.
Since the assignment operator and the copy constructor often have the same effect.
Not at all, one does initialization while the other does assignment. They are different in the initial state of the object, and their tasks are separate (though similar). The canonical assignment operator is usually done as:
Foo& operator=(Foo right) {
right.swap( *this );
return *this;
}
It might not be valid to forward all to an assignment operator, but that is was commmon in C++03 where it was allowed.
In C++11 constructors are easier: forward all the constructors to one master constructor.
class Foo {
private:
int a,b;
public:
Foo(int c, int d) : a(c), b(d) {}
Foo(const Foo & X) : Foo(x.a, x.d) {}
//syntax may be wrong, I don't have a C++11 compiler
Foo& operator=(const Foo& X) {
if (this == &X) return *this;
a = X.a;
b = X.b;
return *this;
}
}
In C++03 (where it is allowed)
class Foo {
private:
int a,b;
void init(int c, int d) {a=c; b=d;}
public:
Foo(int c, int d) : {init(c,d);}
Foo(const Foo & X) : {init(X.a, X.b);}
Foo& operator=(const Foo& X) { init(X.a, X.b);}
}
But keep in mind that this cannot be used in some common cases. (any object that isn't assignable)