std::is_trivially_copyable is too strong, what shall I use instead? - c++

I want to accept classes which are "trivially copyable", in the sense that if I mem-copy the byte representation of one variable of the type into another, it will be usable, and nothing will have been broken in any way.
Looking at std::is_trivially_copyable, I see the requirements are:
Trivially copyable classes, i.e. classes satisfying following requirements:
At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is eligible
Every eligible copy constructor (if any) is trivial
Every eligible move constructor (if any) is trivial
Every eligible copy assignment operator (if any) is trivial
Every eligible move assignment operator (if any) is trivial
Has a trivial non-deleted destructor
First and last requirement - check. But the other requirements are super-strong and not what I want! I'd be willing to "compromise" on having a trivial copy ctor as a requirement, and that's already quite different than what I really want.
So, what type trait can I use from the standard library, or write myself, to express the constraint I'm interested in?
I'm writing C++11 right now, so if your answer requires a later standard - write it, but mention that fact explicitly.

At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is eligible
Has a trivial non-deleted destructor
First and last requirement - check. But the other requirements are super-strong
So, what type trait can I use from the standard library, or write myself, to express the constraint I'm interested in?
You could use (is_copy_constructible || is_move_constructible || is_assignable) && std::is_trivially_destructible.
I'd be willing to "compromise" on having a trivial ... copy ctor as a requirement
You could use std::is_trivially_copy_constructible.
if I mem-copy the byte representation of one variable of the type into another, it will be usable, and nothing will have been broken in any way.
However, those requirements won't be sufficient for this. You need the "super-strong" requirements for this. This is what std::is_trivially_copyable is for.

Related

std::is_trivially_xxx which one imply another

I have hard time to understand following concepts:
is_trivially_copyable
is_trivially_copy_assignable
is_trivially_copy_constructible
is_trivially_destructible
is_trivially_move_assignable
Take for example std::string_view.
#include <string_view>
int main(){
using sv = std::string_view;
// static_assert(std::is_trivial_v<sv> );
static_assert(std::is_trivially_copyable_v<sv> );
static_assert(std::is_trivially_copy_assignable_v<sv> );
static_assert(std::is_trivially_copy_constructible_v<sv> );
static_assert(std::is_trivially_destructible_v<sv> );
static_assert(std::is_trivially_move_assignable_v<sv> );
}
My question is which of these, imply others, so I can to less static_assert's in my code?
I know is_trivial imply all of these,
Note I am not interested in c-tors:
is_trivially_constructible
is_trivially_default_constructible
std::is_trivially_copyable covers the rest, though allows for the relevant methods to be deleted. It says:
If T is a TriviallyCopyable type, provides the member constant value equal true. For any other type, value is false.
And requirements for TriviallyCopyable are:
Every copy constructor is trivial or deleted
Every move constructor is trivial or deleted
Every copy assignment operator is trivial or deleted
Every move assignment operator is trivial or deleted
at least one copy constructor, move constructor, copy assignment operator, or move assignment operator is non-deleted
Trivial non-deleted destructor
You can even check llvm implementation.
This is related to the rule of zero.
AFAIK rest of the test are basic and unrelated to each other in terms of logical implications.
Apart from tests you mention is_copy_assignable and is_copy_constructible (and possibly other "copy" versions) can be formulated in terms of no-copy tests e.g. is_assignable. But this is something different, since checking for "copy" is just adding additional type constraint.

Can I just use memcpy for those std::is_trivially_move_* things?

What is the difference between is_trivially_copy_* and is_trivially_move_*? Can I use memcpy to move construct/assignment is_trivially_move_* types?
The difference between them is exactly what the standard says it is: are the specific operations trivial. A class can have a trivial copy constructor and a non-trivial move constructor or vice-versa.
However, these traits are not sufficient to be substituted with memcpy. The standard only allows you to use memcpy for objects which are TriviallyCopyable. Not merely trivially copy constructible, but TriviallyCopyable in full. So the trait you want is is_trivially_copyable.
And technically, you should also check to see if the type is copy constructible/assignable, depending on whether you're memcpying to live objects or not. A TriviallyCopyable type can have a deleted copy constructor or assignment operator, in which case the writer of that class expects that copying of that form can't happen. The more usual case for a TriviallyCopyable type is a deleted copy assignment operator (due perhaps to having a const member), in which case you should not memcpy to a live object.

Is it safe to rely on an implicitly declared move constructor?

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.

In what scenarios should I expect to explicitly need to implement a move constructor and move assignment operator?

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.

Under what conditions should I be thinking about implementing a move constructor and a move operator?

For standard copy constructors and assignment operators, I always think about implementing them or deleteing the defaults out of existence, if my class implements a destructor.
For the new move constructor and move operator, what is the right way to think about whether or not an implementation is necessary?
As a first pass of transitioning a system from pre-C++0x, could I just delete the default move constructor and move operator or should I leave them alone?
You don't have to worry about it, in the sense that when you user-declare a destructor (or anything else listed in 12.8/9), that blocks the default move constructor from being generated. So there's not the same risk as there is with copies, that the default is wrong.
So, as the first pass leave them alone. There may be places in your existing code where C++11 move semantics allow a move, whereas C++03 dictates a copy. Your class will continue to be copied, and if that caused no performance problems in C++03 then I can't immediately think of any reason why it would in C++11. If it did cause performance problems in C++03, then you have an opportunity to fix a bug in your code that you never got around to before, but that's an opportunity, not an obligation ;-)
If you later implement move construction and assignment, they will be moved, and in particular you'll want to do this if you think that C++11 clients of your class are less likely to use "swaptimization" to avoid copies, more likely to pass your type by value, etc, than C++03 clients were.
When writing new classes in C++11, you need to consider and implement move under the same criteria that you considered and implemented swap in C++03. A class that can be copied implements the C++11 concept of "movable", (much as a class that can be copied in C++03 can be swapped via the default implementation in std), because "movable" doesn't say what state the source is left in - in particular it's permitted to be unchanged. So a copy is valid as a move, it's just not necessarily an efficient one, and for many classes you'll find that unlike a "good" move or swap, a copy can throw.
You might find that you have to implement move for your classes in cases where you have a destructor (hence no default move constructor), and you also have a data member which is movable but not copyable (hence no default copy constructor either). That's when move becomes important semantically as well as for performance.
With C++11, you very rarely need to provide a destructor or copy semantics, due to the way the library is written. Compiler provided members pretty much always do fine (provided they are implemented correctly: MSVC forces you to implement a lot of move semantics by hand, which is very bothersome).
In case you have to implement a custom destructor, use the following approach:
Implement a move constructor, and an assignment operator taking by value (using copy&swap: note that you cannot use std::swap since it uses the assignment. You have to provide a private swap yourself). Pay attention to exception guarantees (look up std::move_if_noexcept).
If necessary, implement a copy constructor. Otherwise, delete it. Beware that non default copy semantics rarely make sense.
Also, a virtual destructor counts as a custom destructor: provide or delete copy + move semantics when declaring a virtual destructor.
Since they are used as an optimization you should implement them if the optimization is applicable to your class. If you can "steal" the internal resource your class is holding from a temporary object that is about to be destroyed. std::vector is a perfect example, where move constructor only assigns pointers to internal buffer leaving the temporary object empty (effectively stealing the elements).