Do compilers provide default move assignment operators and move constructors? - c++11

Do compilers (let us use g++ as the specific example) provide a default move constructor and default move assignment operator when we write a class?
Compilers provide a default:
Constructor (no arguments) unless another constructor with arguments is declared.
Destructor (which presumably does nothing? - actually not quite, this question has an answer here, it calls the base class destructor)
Copy Constructor unless we write our own
Copy Assignment Operator unless we write our own
Will a compiler provide a default move constructor or move assignment operator.

According to cppreference:
Implicitly-declared move constructor
If no user-defined move constructors 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 copy assignment operators
there are no user-declared move assignment operators
there are no user-declared destructors
(until C++14) the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section
then the compiler will declare a move constructor as a non-explicit
inline public member of its class with the signature T::T(T&&).
A class can have multiple move constructors, e.g. both T::T(const T&&)
and T::T(T&&). If some user-defined move constructors are present, the
user may still force the generation of the implicitly declared move
constructor with the keyword default.
And according to cppreference:
Implicitly-declared move assignment operator
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
(until C++14) the implicitly-declared move assignment operator would not be defined as deleted
then the compiler will declare a move assignment operator as an inline
public member of its class with the signature T& T::operator=(T&&).
A class can have multiple move assignment operators, e.g. both T&
T::operator=(const T&&) and T& T::operator=(T&&). If some user-defined
move assignment operators are present, the user may still force the
generation of the implicitly declared move assignment operator with
the keyword default.
Because some assignment operator (move or copy) is always declared for
any class, the base class assignment operator is always hidden. If a
using-declaration is used to bring in the assignment operator from the
base class, and its argument type could be the same as the argument
type of the implicit assignment operator of the derived class, the
using-declaration is also hidden by the implicit declaration.

Related

Is it still necessary to use std move even if auto && has been used

As we know, STL usually offered two kinds of functions to insert an element: insert/push and emplace.
Let's say I want to emplace all of elements from one container to another.
for (auto &&element : myMap)
{
anotherMap.emplace(element); // vs anotherMap.empalce(std::move(element));
}
In this case, if I want to call the emplace, instead of insert/push, must I still call std::move here or not?
If you indeed want to move all elements from myMap into anotherMap then yes you must call std::move(). The reason is that element here is still an lvalue. Its type is rvalue reference as declared, but the expression itself is still an lvalue, and thus the overload resolution will give back the lvalue reference constructor better known as the copy constructor.
This is a very common point of confusion. See for example this question.
Always keep in mind that std::move doesn't actually do anything itself, it just guarantees that the overload resolver will see an appropriately-typed rvalue instead of an lvalue associated with a given identifier.

C++ return value and move rule exceptions

When we return a value from a C++ function copy-initialisation happens. Eg:
std::string hello() {
std::string x = "Hello world";
return x; // copy-init
}
Assume that RVO is disabled.
As per copy-init rule if x is a non-POD class type, then the copy constructor should be called. However for C++11 onward, I see move-constrtuctor being called. I could not find or understand the rules regarding this https://en.cppreference.com/w/cpp/language/copy_initialization. So my first question is -
What does the C++ standard say about move happening for copy-init when value is returned from function?
As an extension to the above question, I would also like to know in what cases move does not happen. I came up with the following case where copy-constructor is called instead of move:
std::string hello2(std::string& param) {
return param;
}
Finally, in some library code I saw that std::move was being explicitly used when returning (even if RVO or move should happen). Eg:
std::string hello3() {
std::string x = "Hello world";
return std::move(x);
}
What is the advantage and disadvantage of explicitly using std::move when returning?
You are confused by the fact that initialization via the move constructor is a special case of "copy initialization", and does not come as seperate concept. Check the notes on the cppreference page.
If other is an rvalue expression, move constructor will be selected by overload resolution and called during copy-initialization. There is no such term as move-initialization.
For returning a value from the function, check the description of returning on cppreference. It says in a box called "automatic move from local variables and parameters", where expression refers to what you return (warning: that quote is shortened! read the original for full details about other cases):
If expression is a (possibly parenthesized) id-expression that names a variable whose type is [...] a non-volatile object type [...] and that variable is declared [...] in the body or as a parameter of the [...] function, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if expression were an rvalue expression (thus it may select the move constructor), and if the first overload resolution failed [...] then overload resolution is performed as usual, with expression considered as an lvalue (so it may select the copy constructor).
So in the special case of returning a local variable, the variable can be treated as r-value, even if normal syntactic rules would make it a l-value. The spirit of the rule is that after the return, you can't find out whether the value of the local variable has been destroyed during the copy-initialization of the returned value, so moving it does not do any damage.
Regarding your second question: It is considered bad style to use std::move while returning, because moving will happen anyway, and it inhibits NRVO.
Quoting the C++ core guidelines linked above:
Never write return move(local_variable);, because the language already knows the variable is a move candidate. Writing move in this code won’t help, and can actually be detrimental because on some compilers it interferes with RVO (the return value optimization) by creating an additional reference alias to the local variable.
So that library code you quote is suboptimal.
Also, you can not implicitly move from anything that is not local to the function (that is local variables and value parameters), because implicit moving may move from something that is still visible after the function returned. In the quote from cppreference, the important point is "a non-volatile object type". When you return std::string& param, that is a variable with reference type.

Can I use const & as parameter of a constexpr function?

I'm writing a constrexpr function taking either a CArray T(&)(N), either a std::array.
I think I have to write 2 functions (if you know better I would be happy to know),
But I'm concerned about what I wrote with the std::array
constexpr float LinInterp01(const std::array<float, N>& inArray, float inX);
Is it correct when writing a constrexpr function to pass by const & or not?
I think it should be because at compile time the compiler would instanciate a copy and there is no notion of L Value, at compile time.
Could someone explain me this?
C++ standard section § 7.1.5 [dcl.constexpr]
The definition of a constexpr function shall satisfy the following constraints:
— it shall not be virtual (10.3);
— its return type shall be a literal type;
— each of its parameter types shall be a literal type;
And section § 3.9 [basic.types]
A type is a literal type if it is:
— void; or
— a scalar type; or
— a reference type; or
— an array of literal type; or
— a class type (Clause 9) that has all of the following properties:
— it has a trivial destructor,
— it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template
that is not a copy or move constructor, and
— all of its non-static data members and base classes are of non-volatile literal types.
So yes, you can pass parameters by reference to constexpr functions.
Now whether or not your function calls will actually be evaluated at compile time depends on the body and calls of LinInterp01.

Contiguous storage containers and move semantics

How can a container be both contiguous and support move semantics at the same time?
An example with std::vector:
When push_back() is called using std::move on an lvalue:
std::vector<MyClass> v;
MyClass obj;
MyClass obj2;
vt.push_back(std::move(obj));
vt.push_back(std::move(obj2));
obj and obj2 are not necessarily allocated next to each other in memory. Now as vector must have its elements in contiguous memory, how would move semantics work in this case? It seems to me that it must copy the obj2 to vector v's contiguous memory region (next to obj), otherwise the contiguosity requirement would not be satisfied. But that requires a copy, not a move. What is the difference between the above and this then?:
MyClass obj;
MyClass obj2;
vt.push_back(std::move(obj));
vt.push_back(obj2);
You just need to read a little bit more about move semantics :-)
Moving an object does not change the address of the object itself. It merely calls the move constructor (or assignment operator, etc. depending on context) of another instance of the object, which is passed the one that is to be moved.
In this example, the vector does create two MyClass objects within its internal storage, one per push_back. But for the first one, instead of calling the copy constructor and passing obj (via MyClass const&), it calls the move constructor and passes the rvalue reference (MyClass&&) to obj. It is then up to that constructor to move the contents of the obj object into the one inside the vector.
In other words, the objects themselves are being created in the vector, and its only their contents that are moved (what it means to 'move' an object is potentially different for each type, hence it's the move constructor's job to do the actual moving).
Note that even with std::move, the move constructor may not be called -- there may not be one, for example, or there may be one that isn't noexcept (in which case std::vector can't use it in all circumstances without violating its exception safety guarantees).
Your std::move is a cast, it's an unconditional cast and it can easily decay in a copy if you are not using it with the proper semantics.
In other words writing std::move doesn't guarantee you anything other than a T&& type which by itself doesn't guarantee you move semantics .

Efficiency of C++11 push_back() with std::move versus emplace_back() for already constructed objects

In C++11 emplace_back() is generally preferred (in terms of efficiency) to push_back() as it allows in-place construction, but is this still the case when using push_back(std::move()) with an already-constructed object?
For instance, is emplace_back() still preferred in cases like the following?
std::string mystring("hello world");
std::vector<std::string> myvector;
myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)
Additionally, is there any benefit in the above example to instead doing:
myvector.emplace_back(std::move(mystring));
or is the move here entirely redundant, or has no effect?
Let's see what the different calls that you provided do:
emplace_back(mystring): This is an in-place construction of the new element with whatever argument you provided. Since you provided an lvalue, that in-place construction in fact is a copy-construction, i.e. this is the same as calling push_back(mystring)
push_back(std::move(mystring)): This calls the move-insertion, which in the case of std::string is an in-place move-construction.
emplace_back(std::move(mystring)): This is again an in-place construction with the arguments you provided. Since that argument is an rvalue, it calls the move-constructor of std::string, i.e. it is an in-place move-construction like in 2.
In other words, if called with one argument of type T, be it an rvalue or lvalue, emplace_back and push_back are equivalent.
However, for any other argument(s), emplace_back wins the race, for example with a char const* in a vector<string>:
emplace_back("foo") calls std::string(char const*) for in-place-construction.
push_back("foo") first has to call std::string(char const*) for the implicit conversion needed to match the function's signature, and then a move-insertion like case 2. above. Therefore it is equivalent to push_back(string("foo"))
The emplace_back gets a list of rvalue references and tries to construct a container element direct in place. You can call emplace_back with all types which the container element constructors supports.
When call emplace_back for parameters which are not rvalue references, it 'falls back' to normal references and at least the copy constructor ist called when the parameter and the container elements are of the same type.
In your case 'myvector.emplace_back(mystring)' should make a copy of the string becaus the compiler could not know that the parameter myvector is movable. So insert the std::move what gives you the desired benefit.
The push_back should work as well as emplace_back for already constructed elements.

Resources