The explicit keyword is recommended for all most constructors which can be called with one argument, except for copy constructors.
For copy constructors, it has an use (to forbid implicit copying via function call, return, etc), but it's not what's usually wanted.
What about move constructors? Is there any reasonable use case to make them explicit? What's the good practice here?
An explicit move constructors can affect compatibility with e.g. Standard algorithms. For instance, std::swap<T> requires that T be MoveConstructible. In turn, MoveConstructible is specified in terms of an expression, namely T u = rv; (where rv is an rvalue of type T).
If there is neither a non-explicit copy constructor nor a non-explicit move constructor for a given type then T u = rv; is invalid and that type can't be used with std::swap. (In this particular instance however it is possible to specialize std::swap to provide the desired functionality, e.g. by using T u(rv);).
Put more simply, an explicit move or copy constructor defies expectations and can't be used as well with generic code.
Some other parts of the Standard library that put a MoveConstructible requirement:
the deleter of unique_ptr<T, D>
call wrappers, used in e.g. bind (all the decayed types that are passed are concerned)
thread, async, call_once (all specified in terms of call wrappers)
sort, stable_sort, nth_element, sort_heap
The explicit keyword is recommended for (single argument) converting constructors, to avoid surprising conversions in unexpected places.
Copy constructors and move constructors are hardly "surprising" in that sense. They happen largely where expected. If you don't want them, I would expect them to be marked =delete rather than made explicit.
You probably want an implicit move constructor for the majority of uses. They generally fall into the same categories as copy constructors. Explicit isn't recommended for all one-argument constructors, but it is recommended for most. Move constructors are not on that list.
The actual question is how explicit move constructor could possibly be used? It wouldn't be able to be invoked on rvalues, so compiler would have to always select a copy constructor, if available, or fail to compile.
Edit: here is the link to example: http://www.ideone.com/nm7KM
When returning by value from a function, an implicit move constructor can usually make the process more efficient.
Related
This question already has answers here:
How does guaranteed copy elision work?
(2 answers)
What are copy elision and return value optimization?
(5 answers)
Closed 5 years ago.
I learned, long ago, that copy initialization creates a temporary which is then used to initialize the destination, though the latter copy constructor is optimized out; but the compiler still pretends to use it, checking for existence and allowed access.
I noticed Herb Sutter mention, in updated GOTW posts, that this is no longer true when auto is used.
Specifically, Herb (writing in 2013) still states the familiar rules in general:
If x is of some other type, conceptually the compiler first implicitly converts x to a temporary widget object… Note that I said “conceptually” a few times above. That’s because practically compilers are allowed to, and routinely do, optimize away the temporary — from GOTW #1
He later notes that when auto is used (in auto w = x;) that only a single copy constructor is called because there is no way that x needs to be converted first.
In the case of a function returning an iterator (so it’s an rvalue):
After all, as we saw in GotW #1, usually that extra = means the two-step “convert to a temporary then copy/move” of copy-initialization—but recall that doesn’t apply when using auto like this. … presto! there cannot be any need for a conversion and we directly construct i. —GOTW #2 (emphasis mine)
If the type returned from the function was exactly the same type as the variable being initialized with copy initialization, the rule would be that the temporary gets optimized out but it still checks access on the copy constructor. Herb is saying that this is not the case with auto, but direct initialization is used.
There are other examples where he seems to be saying (though not with rigorous precision) that when using auto you no longer have the compiler pretend to use the copy constructor.
What’s going on? Has the rule changed? Is this some additional feature of auto that all the presentations have missed mentioning?
I examined the C++17 standard (n4659) and could not find any special mention of auto in the section on initialization, nor anything about initialization in the section on auto. So, I went back to basics and read the rules for initialization in detail. And boy, has it changed! In C++17, the meaning of copy-initialization is not what you learned earlier. The many answers here on SO, and tutorials that explain how the copy constructor is needed but then optimized out are all obsolete, and now wrong.
C++17 has changed the way temporaries are handled, in order to support more copy elision and to enable a way to explain mandatory copy elision. In a nutshell, prvalues are not temporaries that may be optimized away but are still logically copied (or moved) to their final home. Instead, prvalues are sort of in limbo, with no address and no real existence. If a temporary is actually needed, one is “materialized”. But the idea here is that the prvalue can be collapsed out and the code that creates a value (such as the return statement) can be matched up with the final target, avoiding the creation of a temporary.
You can see that this directly affects the meaning of initialization, and by itself cuts out the old rule about a temporary in copy initialization.
So here’s the deal:
the new initialization rules
direct-initialization vs copy-initialization
This description covers the case where an object of class type is being defined. That is, not a reference, not a primitive type, etc. To keep the description simple, I’m not mentioning move constructors every place that copy constructor is mentioned.
C c1 ( exp() ); //exp returns a value, not a reference.
C c2 (v);
C c3 = exp();
C c4 = v;
We have direct initialization (c1 and c2) and copy initialization (c3 and c4). The list forms will be covered separately.
The following cases are covered in order of priority. Each rule is described assuming that earlier rules did not match already.
prvalue
If the source is a prvalue (pure rvalue) and already the right type, we get the match-up between the place that created the value and the final destination. That is, in c1 and c3, the value created by the return statement in exp will be created directly in the variable. In underlying machine language, the caller determines where the return value will go and passes this to exp as another parameter or a dedicated register or whatnot. Here, the address where c1 (or c3) will go is passed for this purpose. It always did that (in optimized code); but logically the caller set up a temporary and then copied the temporary to c3, but was allowed to optimize out the copy. Now, there is no temporary. The direct pipeline from value creation inside the function to the declared variable in the caller is part of the specification.
Note that this new reality affects prvalues in general. A prvalue (pure rvalue) is what you have when a function returns an object by value. The passage that explains this case (§11.6 ¶17.6.1) doesn’t even mention copy vs direct forms!
This means that the copy constructor is not involved. Assuming the function had some way to create the object (a different constructor or private access), you can create a variable when you don’t have an accessible copy constructor at all. This can be handy for factories, where you want to control how objects are created and the type is not copyable and not movable.
direct, and sometimes copy
In direct-initialization (c2), the parameters are used to call a constructor. Being direct, explicit constructors are considered. This should be familiar enough.
In copy-initialization, if the value is the same class as the destination or even a derived class, the value is used as a parameter to a constructor. That is c4 where v is also of class C, or class D derived from C so it is-a C. We suppose that would be the copy constructor that’s chosen, but you can have special constructors for derived types. Being copy-initialization, explicit constructors are ignored. The copy constructor can’t be explicit anyway. But you could have C::C(const D&) that’s explicit!
copy
In remaining cases of copy initialization, some conversion is found. For c4 suppose v is of type E. The conversion functions are conversion operators defined in E (e.g. E::operator C()) and non-explicit constructors in C (e.g. C::C(const E&)). The result of the conversion is then used with direct-initialization. That sounds familiar… but here is the tricky part: the conversion function might produce a prvalue! A normal conversion operator will return by value, so the return value from that operator is constructed directly in c4. It’s possible to write a conversion operator that returns a reference, and is thus an lvalue. But converting constructors are always prvalues. So, it appears that if a constructor is used then copy-initialization is just as direct as direct-initialization. But now it’s a phantom prvalue, not a temporary, that goes away.
list initialization
C c5 { v1,foo(),7 };
C c6 = { v1,foo(),7 };
C c7 = {exp()};
First of all, if C has a special constructor for initialization lists, then that is used. None of the regular stuff applies.
Otherwise, it’s pretty much the same as before. There are a few differences (§11.6.4 ¶3.6):
Because of the new syntax, it’s possible to specify multiple arguments for a constructor in the copy-initialization form. For example, c5 and c6 both specify the same constructor arguments. In copy-initialization the explicit constructors are disallowed. Note that I wrote disallowed not ignored: In regular copy-initialization, explicit constructors are ignored and overload resolution uses only the non-explicit forms. But in copy-list-initialization, all constructors are used for overload resolution, but then if an explicit constructor is chosen you get an error (§16.3.1.7).
Another difference is that when using the list syntax, narrowing conversions (§11.6.4 ¶7) are flagged as errors if they would be used in implicit conversions.
Now for a big surprise: the temporary is back! in case c7, even though exp() is a prvalue, the rule is to match that to constructor arguments. So, the expression produced a value of type C, but then the value is used to pick a constructor (which will be the copy constructor). Since the copy constructor cannot be explicit, there is no difference between the direct and copy syntax.
What does a normal programmer need to know?
This is all rather complex, but a normal day-to-day coder only needs to understand a few simple rules.
prvalues are piped directly to the new home. So the details happened in the function return, and there really isn’t anything happening here. So, there is no meaningful distinction between copy and direct syntax.
copy-initialization vs direct-initialization affects explicit constructors.
direct-initialization specifically asks for a constructor; copy-initialization can use any way of converting.
for lists:
list-initialization can use a special list constructor (e.g. std::vector).
list-initialization gives constructor arguments, but doesn’t allow narrowing conversions.
So, there’s the new part about lists. But really, it’s something you need to unlearn: forget about the convert-then-(pretend to)copy business.
This is where I got most of this information: http://en.cppreference.com/w/cpp/language/move_constructor
Apparently these are the conditions for the implicitly generated move constructor to work:
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
the implicitly-declared move constructor is not defined as deleted
if a user declared move constructor is present, it is still possible to still force the generation of the implicitly declared move constructor with the keyword default
My questions are:
Is it safe to rely on implicit automatic move constructor?
How do I check if it really worked instead of default copy constructor?
Finally, and most importantly, is it a good idea and why? Or is it always better to define my own?
I am more inclined to follow the rule of three and manually create a destructor, a copy and move constructor, and a copy and move assignment operator, but I'm just curious about this implicit one.
Here are the answers to your questions:
What do you mean with "safe"? When the rules apply, i.e., the subobjects are movable and you didn't do anything to stomp on the generation of the move constructor, it will be created and used when present. Note, however, that it is easy to have a non-movable subobject which will somewhat invisibly inhibit the creation of a move constructor.
To see if your class got a move constructor, just temporarily add an empty base logging when the copy and the move constructors are used and force the object to be moved/copied: it will log the correspondingly used constructor.
No code is generally better than any code.
1. Is it safe to rely on implicit automatic move constructor?
Nothing is safe to rely upon without testing (implicit or explicit).
2. How do I check if it really worked instead of default copy constructor?
Testing. See the example test below.
3. Finally, and most importantly, is it a good idea and why? Or is it
always better to define my own?
There are distinct (and growing) advantages to making your special members trivial. A trivial special member is one defined/supplied by the compiler. You can declare a trivial member with = default. Actually that last sentence is an exaggeration. If you declare a special member with = default, it won't for sure be trivial. That depends on your members and bases. But if you define a special member explicitly (as in C++98/03), then for sure it will not be trivial. If you have a choice between user-provided and trivial, prefer trivial.
Furthermore, you don't need to test if your type X has a move constructor. You need to test that if you move construct your X, that it has the right exception safety, and the right performance. If X::X(const X&) accomplishes that task, then so be it. In that case X::X(X&&) is not necessary.
If you expect that your type X will have a throwing copy constructor, and a much faster noexcept move constructor, here is a really nice test to confirm it is so:
static_assert(std::is_nothrow_move_constructible<X>::value,
"X should move construct without an exception");
Put this test right in your source/header. Now, no matter whether you implicitly, or explicitly declare or define your special members, you've got a concrete compile-time test that is practically zero cost. The static_assert generates zero code, and consumes a negligible amount of compile time.
Somewhat related to Why is copy constructor called instead of conversion constructor?
There are two syntaxes for initialization, direct- and copy-initialization:
A a(b);
A a = b;
I want to know the motivation for them having different defined behavior. For copy initialization, an extra copy is involved, and I can't think of any purpose for that copy. Since it's a copy from a temp, it can and probably will be optimized out, so the user can't rely on it happening - ergo the extra copy itself isn't reason enough for the different behavior. So... why?
Only a speculation, but I am afraid it will be hard to be more certain without Bjarne Stroustrup confirming how it really was:
It was designed this way because it was assumed such behaviour will be expected by the programmer, that he will expect the copy to be done when = sign is used, and not done with the direct initializer syntax.
I think the possible copy elision was only added in later versions of the standard, but I am not sure - this is something somebody may be able to tell certainly by checking the standard history.
Since it's a copy from a temp, it can and probably will be optimized out
The keyword here is probably. The standard allows, but does not require, a compiler to optimize the copy away. If some compilers allowed this code (optimized), but others rejected it (non-optimized), this would be very inconsistent.
So the standard prescribes a consistent way of handling this - everyone must check that the copy constructor is accessible, whether they then use it or not.
The idea is that all compilers should either accept the code or reject it. Otherwise it will be non-portable.
Another example, consider
A a;
B b;
A a1 = a;
A a2 = b;
It would be equally inconsistent to allow a2 but forbid a1 when As copy constructor is private.
We can also see from the Standard text that the two methods of initializing a class object were intended to be different (8.5/16):
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.
A difference is that the direct-initialization uses the constructors of the constructed class directly. With copy-initialization, other conversion functions are considered and these may produce a temporary that has to be copied.
Take the following example:
struct X
{
X(int);
X(const X&);
};
int foo(X x){/*Do stuff*/ return 1; }
X x(1);
foo(x);
In the compilers I tested, the argument to foo was always copied even with full optimization turned on. From this, we can gather that copies will not/must not be eliminated in all situations.
Now lets think from a language design perspective, imagine all the scenarios you would have to think about if you wanted to make rules for when a copy is needed and when it isn't. This would be very difficult. Also, even if you were able to come up with rules, they would be very complex and almost impossible for people to comprehend. However, at the same time, if you forced copies everywhere, that would be very inefficient. This is why the rules are the way they are, you make the rules comprehensible for people to understand while still not forcing copies to be made if they can be avoided.
I have to admit now, this answer is very similar to Suma's answer. The idea is that you can expect the behavior with the current rules, and anything else would be too hard for people to follow.
Initialization of built-in types like:
int i = 2;
is very natural syntax, in part due to historical reasons (remember your high school math). It is more natural than:
int i(2);
even if some mathematicians may argue this point. After all, there is nothing unnatural in calling a function (a constructor in this case) and passing it an argument.
For built-in types these two types of initialization are identical. There is no extra copy in the former case.
That is the reason for having both types of initialization and originally there was no specific intention to make them behave differently.
However, there are user-defined types and one of the stated goals of the language is to allow them to behave as built-in types as closely as possible.
Thus, copy construction (taking input from some conversion function, for example) is the natural implementation of the first syntax.
The fact that you may have extra copies and that they may be elided is an optimization for user-defined types. Both copy elision and explicit constructors came much later into the language. It is not surprising that standard allows optimizations after a certain period of use. Also, now you can eliminate explicit constructors from the overload resolution candidates.
Given that a class actually is moveable, manually implementing the move constructor and move assignment operator for a class quickly become tedious.
I was wondering when doing so is actually a heavy, heavy, premature optimization?
For instance, if a class only has trivial POD data or members that themselves have move constructor and move assignment operator defined, then I'd guess that the compiler will either just optimize the shit out of the lot (in the case of PODs) and otherwise use the members' move constructor and move assignment operator.
But is that guaranteed? In what scenarios should I expect to explicitly need to implement a move constructor and move assignment operator?
EDIT: As mentioned below by Nicol Bolas in a comment to his answer at https://stackoverflow.com/a/9966105/6345, with Visual Studio 11 Beta (and before) no move constructor or move assignment operator is ever automatically generated. Reference: http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
If you find yourself implementing, any of:
destructor
copy constructor
copy assignment
Then you should be asking yourself if you need to implement move construction. If you "= default" any of the above, you should be asking yourself if you should then also "= default" the move members.
Even more importantly, you should be documenting and testing your assumptions, for example:
static_assert(std::is_nothrow_default_constructible<A>::value, "");
static_assert(std::is_copy_constructible<A>::value, "");
static_assert(std::is_copy_assignable<A>::value, "");
static_assert(std::is_nothrow_move_constructible<A>::value, "");
static_assert(std::is_nothrow_move_assignable<A>::value, "");
static_assert(std::is_nothrow_destructible<A>::value, "");
First, move semantics only help for classes that hold resources of any kind. "Flat" classes don't benefit from it at all.
Next, you should build your classes out of "building blocks", like vector, unique_ptr and the likes, that all deal with the nitty-gritty low-level detail of resources. If your class is done as such, you won't have to write anything at all since the compiler will generate the members correctly for you.
If you need to write a destructor for, say, logging, generation of move ctors will be disabled, so you need a T(T&&) = default; for compilers that support it. Otherwise, this is one of the only places were to write such a special member yourself (well, except if you write such a "building block").
Note that the logging in the destructor and constructor can be done an easier way. Just inherit from a special class that logs on construction / destruction. Or make it a member variable. With that:
tl;dr Let the compiler generate the special member for you. This also counts for copy constructor and assignment operator, aswell as the destructor. Don't write those yourself.
(Okay, maybe the assignment operators, if you can identify them as a bottle neck and want to optimize them, or if you want special exception safety or somesuch. Though, the "building blocks" should already provide all that.)
Do it every time the default behavior is undesirable or every time the default ones have been deleted and you still need them.
The compiler default behavior for move is call the member and base move. For flat classes / buil-in types this is just like copy.
The problem is typically present with classes holding pointers or value representing resources (like handle, or particular indexes etc) where a move requires to copy the values in the new place, but also to set the old place to some "null state value" recognizable by the destructor. In all other cases, the default behavior is OK.
The problem may also arise when you define a copy (and the compiler deletes the default move) or a move (and the compiler deletes the default copy), and you need them both. In these cases, re-enabling the default may suffice.
In what scenarios should I expect to explicitly need to implement a move constructor and move assignment operator?
Under the following cases:
When you are using Visual Studio 10 or 11. They implement r-value references, but not compiler generated move semantics. So if you have a type that has members that need moving or even contains a moveable type (std::unique_ptr, etc), you must write the move yourself.
When you might need copy constructors/assignment operators and/or a destructor. If your class contains something that made you manually write copy logic or needs special cleanup in a destructor, odds are good that you'll need move logic too. Note that this includes deleting copy mechanisms (Type(const Type &t) = delete;). If you don't want to copy the object, then you probably need move logic or to delete the move functions too.
As others have said, you should try to keep the number of types that need explicit move or copy mechanisms to a bare minimum. Put these in utility "leaf" classes, and have most of your classes rely on the compiler-generated copy and move functions. Unless you're using VS, where you don't get those...
Note: a good trick with move or copy assignment operators is to take them by value:
Type &operator=(Type t) { std::swap(*this, t); return *this; }
If you do this, it will cover both move and copy assignment in one function. You still need separate move and copy constructors, but it does reduce the number of functions you have to write to 4.
The downside is that you're effectively copying twice if Type is made of only basic types (first copy into the parameter, second in swap). Of course, you have to implement/specialize swap, but that's not difficult.
I'm sorry. I may have missed the point of your question. I'm taking your question to mean copy constructors.
Back in the 1990s when I learned C++, I was taught always to write a copy constructor when designing a class. Otherwise, and this may have changed with newer versions of the compiler, C++ will generate a copy constructor for you in the situations that require one.
That default copy constructor may not always work the way you want. This would especially be true if your class contains pointers, or you otherwise do not want the default byte-wise copy of a default copy constructor.
Finally, I was taught that by writing a copy constructor, you are taking exact control over how you want your class copied.
I hope this helps.
A few times, when refactoring code, I have forgotten to add the explicit keyword when adding a parameter to a previously-parameterless constructor, or removing parameters from a previously multi-parameter constructor. To prevent this, I have gotten in the habit of marking every constructor explicit, no matter no many arguments it has. (Except, of course, those constructors for which I actually want implicit conversion.)
Is there any downside to this? Performance? Compile time?
It doesn't have any downsides. It will be future safe, because in C++0x multi-parameter constructors participate in initializations using multi-element initializer lists and can forbidden to be used in the cases where only implicit conversions apply using explicit.
So if you find out a give multi-parameter constructor does not logically represent the value of your class, I think it's good to make it explicit (example: I would set a container constructor (size_t size, T defaultValue) be explicit, while the constructor of pair, (T first, U second) be set non-explicit).
I'm not sure, but I think it does have some unexpected consequences for the copy constructor to be explicit. Other than that, I think yo're OK.
There will be no runtime performance difference. The compile time difference is likely to be undetectable.
I think there is no harm in declaring all constructors with arguments explicit, except that it might look redundant for those with more than one argument.
If you declare a type with an explicit default constructor, you may have trouble using it with collection types.