Implicitly-declared move assignment operator - c++11

I'm having trouble getting my head around this simple rule from cpp reference;
If no user-defined move assignment operators are provided for a class
type (struct, class, or union), and all of the following is true:
there are no user-declared copy constructors;
there are no user-declared move constructors;
there are no user-declared copy assignment operators;
there are no user-declared destructors;
the implicitly-declared move assignment operator would not be defined
as deleted, (until C++14)
THEN the compiler WILL declare a move assignment operator as an inline public member of its class with the signature T& T::operator=(T&&).
With this in mind, consider
struct bar
{
bar(int i) : _i(i) {};
bar (const bar& other) { };
bar (bar& other) { };
int _i;
};
Followed by this, say;
bar b2(2);
bar b3(3);
cout << "b3._i " << b3._i << endl << endl;
b3 = std::move(b2);
cout << "b3._i " << b3._i << endl;
The output we get is;
b3._i 3
b3._i 2
So here we have a move happening;
b3 = std::move(b2);
I didn't define that move assignment operator, so its been implicitly defined for me by the compiler. However I have broken the conditions laid down, I HAVE a user defined copy ctor.... however there is still a move happening by the compiler generated one. I'm clearly misinterpreting the text, can anybody be so good as to enlighten me?
Thanks and have a nice day.
G

So here we have a move happening;
b3 = std::move(b2);
Nope, this is a copy. std::move(b2) simply casts b2 to bar&&. bar&& then binds to const bar& in the implicitly-generated copy-assignment operator.
live example on wandbox

Related

Does emplace_back construct an object in its new location instead of using a move?

From this link it states
For example, in the code that we began with, my_vec.push_back("foo")
constructs a temporary string from the string literal, and then moves
that string into the container, whereas my_vec.emplace_back("foo")
just constructs the string directly in the container, avoiding the
extra move. For more expensive types, this may be a reason to use
emplace_back() instead of push_back(), despite the readability and
safety costs, but then again it may not. Very often the performance
difference just won’t matter
So I decided to try that and this is what i did
class foo
{
public:
int counter;
foo()
{
std::cout << "Regular constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.push_back(foo()); //Regular constructor and Move Constructor
f.emplace_back(foo()); //Regular constructor and Move Constructor
}
I noticed that both push_back and emplace_back behave similarly. I was thinking that emplace_back will only be calling the regular constructor based on what I read since it will be constructed in the vector stack.
vector::emplace_back(Args&&...) takes the arguments of the constructor you want to construct your new object with. In your quoted example this is const char* for the constructor string::string(const char*). In your own code you're forcing the move constructor by passing a temporary object. To default-construct your object in-place use f.emplace_back() without any arguments as the default constructor takes none.
Also to avoid reallocation (potentially more moves that would spoil your test) ensure the vector has space for your two test objects first using f.reserve(2).
Full code:
class foo
{
public:
foo()
{
std::cout << "Default constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.reserve(2);
f.push_back(foo());
f.emplace_back();
}
Output is
Default constructor
Move constructor
Default constructor

forwarded argument is not moved. why? [duplicate]

Sample program:
#include <iostream>
#include <string>
#include <vector>
template <typename T>
void print(const T& _vec)
{
for( auto c: _vec )
std::cout << c << ",";
}
typedef std::vector<std::string> vecstr_t;
struct Trade
{
explicit Trade(vecstr_t&& vec) : _vec(vec )
{
}
vecstr_t _vec;
};
int main()
{
vecstr_t tmpV = {"ONE", "TWO", "THREE", "FOUR"};
std::cout << "size 1:" << tmpV.size() << "\t"; print(tmpV); std::cout << "\n" ;
Trade t(std::move(tmpV));
std::cout << "size 2:" << tmpV.size() << "\t"; print(tmpV); std::cout << "\n" ; // expted tmpV should be e,pty but it has original contents
print(t._vec);
}
I expect size 2: should be ZERO but output is:
size 1:4 ONE,TWO,THREE,FOUR,
size 2:4 ONE,TWO,THREE,FOUR,
ONE,TWO,THREE,FOUR,
explicit Trade(vecstr_t&& vec) : _vec(vec)
{}
In the constructor above, even though vec is of type rvalue reference to vecstr_t, it is itself an lvalue. The basic rule to remember is - if it has a name, it's an lvalue.
There are very few contexts where an lvalue may automatically be moved from (such as the return statement of a function that returns an object by value), but a constructor's mem-initializer list is not one of them.
In your example, _vec is copy constructed from vec. If you want it to be move constructed instead, use std::move.
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec))
{}
Now the second call to print will not print anything. Note that technically the second call could print a non-zero size because the contents of a moved from vector are unspecified. But on most (probably all) implementations, you'll see an empty vector.
Live demo
Your comment below says your intent is to accept both rvalues and lvalues, move only in the case of the former, and copy the argument otherwise. As currently written, your constructor will only accept rvalues, and not lvalues. There are a few different options to achieve what you want.
The easiest probably is to change the parameter so that it's taking the argument by value, and then unconditionally move.
explicit Trade(vecstr_t vec) : _vec(std::move(vec))
{}
The drawback with this approach is that you may incur an additional move construction of the vector, but move constructing a vector is cheap, and you should go with this option in most cases.
The second option is to create two overloads of the constructor
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec)) {}
explicit Trade(vecstr_t const& vec) : _vec(vec) {}
The drawback with this one is that the number of overloads will increase exponentially as the number of constructor arguments increases.
The third option is to use perfect forwarding.
template<typename V>
explicit Trade(V&& vec) : _vec(std::forward<V>(vec)) {}
The code above will preserve the value category of the argument passed to the constructor when it forwards it to construct _vec. This means that if vec is an rvalue, the vecstr_t move constructor will be called. And if it is an lvalue, it will be copied from.
The drawback with this solution is that your constructor will accept any type of argument, not just a vecstr_t, and then the move/copy construction in the mem-initializer list will fail if the argument is not convertible to vecstr_t. This may result in error messages that are confusing to the user.

C++ why overloading (T&) in template with (T*)

in C++, if a method is accepting left reference + pointer only,
it seems it suffices if we only have a template method with T& as its parameter, why we usually overload with test(T* ) as well ?
proof of concept: left reference method can take pointer argument.
#include <iostream>
using namespace std;
template<class T>
void test(T& arg) {
T value = arg;
cout << *value << endl;
}
int main() {
int b = 4;
int* a = &b;
test(a); // compiles and runs without issue.
return 0;
}
Why [do] we usually overload with test(T* ) as well?
I am not sure that we usually do anything of the sort, but if one were to overload for a pointer, it would be because pointers behave differently than object types. Remember, a pointer in fact is not an object but an address to an object.
The reason that test(a) compiles and runs without issue is because it is accepting a reference to a pointer to an object as its parameter. Thus, when the line cout << *value << endl; executes, the pointer is dereferenced back to an object and we see 4 printed to standard out.
As #HolyBlackCat mentioned, we usually want do different things for T& and T*.
As indicated in the example, for test(T&) we usually need to manually do dereference, this would result in the difference in the behavior, so it makes sense to have a overload like this.

behavior of synthesised move constructor

I am reading C++ Primer 5th edition and get the following problems. The book lists several cases that a synthesized move operation is defined as deleted. One of which is "Unlike the copy constructor, the move constructor is defined as deleted if the class has a member that defines its own copy constructor but does not also define a move constructor, or if the class has a member that doesn't define its own copy operations and for which the compiler is unable to synthesize a move constructor. Similarly for move-assignment."
and also provide an demo code as following:
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
However, for both gcc 7.2.1 and clang-900.0.37, the code is runnable, is the book wrong?
Here is the complete test code:
#include <iostream>
struct Y {
Y() { std::cout << "Y()" << std::endl; }
Y(const Y&) { std::cout << "Y(const Y&)" << std::endl; }
//Y(Y&&) { cout << "Y(Y&&)" << endl; }
};
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
int main() {
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
return 0;
}
The book correctly describes the behavior prescribed by the C++11 standard. The prescribed behavior, however, has changed as of C++14, which adopted the resolution of Defect Report #1402 "Move functions too often deleted"

move constructor called on return instead of copy

Today I found that this code doesn't work as I expect it to work.
According to my knowledg for L-values copy constructor should be called while for R-values it should choose move constructor. Otherwise what's the purpose of std::move which actually does nothing but casts to R-value. I was expecting that return obj will call copy constructor, but it calls move.
I understand that copy is useless here, but that's about rules. What if my copy constructor has a side effect and that's my case (I know that it shouldn't, but technically it can - for example here: std::cout call).
Is there anything in standard that allows such behavior? Also how can I force a copy?
#include <iostream>
class X
{
public:
X() = default;
X(const X& r): i(r.i)
{
std::cout << "copy ctor" << std::endl;
}
X(const X&& r): i(r.i)
{
std::cout << "move ctor" << std::endl;
}
int i = 0;
};
X foo()
{
X obj;
obj.i = 10;
return obj;
}
int main()
{
X x = foo();
}
move ctor
move ctor
From cppreference (emphasis mine):
If [the returned expression] is an lvalue expression and the conditions for copy elision are met, or would be met, except that [the returned expression] names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if [the returned expression] were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue [returned expression] (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of [the returned expression] (copy elision requires same type)
Long story short, return implicitly tries to move what you return when it makes sense. It will only copy as a last resort (no move constructor available, for example).

Resources