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 :)
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)
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;
}
I'm trying to sort a vector containing objects that are not copy constructible or default constructible (but are move constructible), but I get errors about the compiler not being able to find a valid function for swap. I thought that having a move constructor would be enough. What am I missing here?
class MyType {
public:
MyType(bool a) {}
MyType(const MyType& that) = delete;
MyType(MyType&& that) = default;
};
int main(void) {
vector<MyType> v;
v.emplace_back(true);
sort(v.begin(), v.end(), [](MyType const& l, MyType const& r) {
return true;
});
}
You need to explicitly define a move assignment operator, as this is what std::sort also tries (not just move construction). Note that the compiler-generation of a move assignment operator is prohibited by the existence of a user-provided copy constructor, as well as by the existence of a user-provided move constructor (even if they are delete-ed). Example:
#include <vector>
#include <algorithm>
class MyType {
public:
MyType(bool a) {}
MyType(const MyType& that) = delete;
MyType(MyType&& that) = default;
MyType& operator=(MyType&&) = default; // need this, adapt to your own need
};
int main(void) {
std::vector<MyType> v;
v.emplace_back(true);
std::sort(v.begin(), v.end(), [](MyType const& l, MyType const& r) {
return true;
});
}
Live on Coliru
The slides by Howard Hinnant (the main contributor to move semantics in C++11) are super useful, as well as Item 17: Understand special member function generation from Effective Modern C++ by Scott Meyers.
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.
A friend of mine stumbled on a question
Is there any way to return a copy of an object which is copy-able but NOT move-able. In other words, can we make the following code work?
struct A {
A() = default;
A(A const&) = default; // copyable
A& operator=(A const&) = default; // assignable
A(A &&) = delete; // not movable
A& operator=(A &&) = delete; // not movable
~A() = default;
};
A foo() {
A a;
return a;
}
int main() {
A a(foo()); //will fail, because it calls A(A&&)
return 0;
}
In my opinion, we can't, because foo() is an A&& an then the compiler has to call A(A&&). But I would like some kind of confirmation.
Following on from the code in the posted comment by dyp; Is there a cast (or standard function) with the opposite effect to std::move?, the following code snippet compiles.
I'm not sure on the usefulness of the code, there was some discussion on whether this was a good idea or not in that linked post.
struct A {
A() = default;
A(A const&) = default; // copyable
A& operator=(A const&) = default; // assignable
A(A &&) = delete; // not movable
A& operator=(A &&) = delete; // not movable
~A() = default;
};
template <typename T>
T& as_lvalue_ref(T&& t) {
return t;
}
A foo() {
A a;
return as_lvalue_ref(a);
}
int main() {
A a(as_lvalue_ref(foo()));
return 0;
}
Code sample here.
A static_cast<T&>() would also work in place of the as_lvalue_ref() and may even be preferable (although looking clumsy) given that it is in fact verbose. In any situation where a reference is returned, dangling references can happen. Given the signature A foo() in this case (instead of say A& foo()), there are no dangling references here, a full object of A returned. The line A a(as_lvalue(foo())); has no dangling references either since the temporary (prvalue) returned from as_lvalue_ref(foo()) remains valid until the end of the expression (;) and then the a object will be then well formed.
Someone came up in another forum with that code. It should not lead to potential dangling references, which may be the case with Niall's code. But is still convoluted for something that was legal in C++03
#include <iostream>
#include <functional>
struct A {
A() =default;
A(A const& a)=default;
A& operator=(A const& a)=default;
A(A &&) = delete; // not movable
A& operator=(A &&) = delete; // not movable
~A()=default;
int n;
};
A foo(void) {
A a;
a.n=5;
std::cout<<"&a="<<&a<<std::endl;
return std::cref(a); // A(A const&)
// ~A()
}
int main(void) {
A b; // A()
std::cout<<"&b="<<&b<<std::endl;
A const& const_ref_to_temp=foo(); // OK in C++
b= const_ref_to_temp; //A=A const& [OK]
std::cout<< b.n<<std::endl;
return 0;
}