BDE 4.14.0 Production release
|
Macros | |
#define | BSLMF_ISBITWISEMOVEABLE_NO_SUPPORT_FOR_ARRAY_OF_UNKNOWN_BOUND 1 |
Provide a primitive type trait for bitwise moveable classes.
This component provides a single trait metafunction, bslmf::IsBitwiseMoveable
, which allows generic code to determine whether t_TYPE
can be destructively moved using memcpy
. Given a pointer, p1
, to an object of t_TYPE
, and a pointer p2
of the same type pointing to allocated but uninitialized storage, a destructive move from p1
to p2
comprises the following pair of operations:
An object of a t_TYPE
is bitwise moveable, if the above operation can be replaced by the following operation without affecting correctness:
If IsBitwiseMoveable<t_TYPE>::value
inherits from true_type
for a given t_TYPE
, then a generic algorithm can infer that t_TYPE
is bitwise moveable.
This trait is used by various components for providing optimizations for types that can be bitwise moved. The major benefit of this trait is not for a single object but for an array of such types, as a loop of copy/destroy operations can be replaced by a single call to memcpy
. This replacement is not only faster, but is guaranteed not to throw an exception.
IsBitwiseMoveable<t_TYPE>
will inherit from true_type
if t_TYPE
is a fundamental object type, enumeration type, or pointer type. Most user-defined classes are bitwise moveable, but generic code must assume that an arbitrary t_TYPE
is not bitwise-moveable, as bitwise moving a type that is not bitwise moveable is likely to result in a dangling pointer. Thus, it is necessary to explicitly associate the bitwise moveable trait with a class (via template specialization or by use of the BSLMF_DECLARE_NESTED_TRAIT
macro) in order for generic algorithms to recognize that class as bitwise moveable. As a special case, one-byte objects are deduced as bitwise moveable unless explicitly annotated otherwise (see-below).
A class that has any of the following attributes is not bitwise moveable:
Because of the destructive nature of a bitwise move (the original object must be treated as uninitialized storage after the move), a class can be bitwise moveable but not also bitwise copyable. For example, a class that contains a pointer to heap-allocated storage is generally bitwise moveable. The moved object simply refers to the same storage as the (defunct) original. However a bitwise copy of the same object would incorrectly cause the original and the copy to share the same heap-allocated storage.
An object whose size does not exceed one byte are deduced to be bitwise moveable. The validity of this heuristic can be deduced by examining the criteria for non-bitwise moveable classes above:
The purpose of this heuristic is to deduce bitwise moveability for an important category of empty classes that are not explicitly annotated as being bitwise moveable: standard predicate classes such as std::less<T>
. Being able to treat these classes as bitwise moveable means that bsl::set
and bsl::map
objects can be deduced as bitwise moveable and that bsl::function
objects wrapping these classes can use the small-object optimization. It can be argued that any type with size less than the size of a pointer should be deduced as bitwise moveable by the logic above. However, it is primarily the common case of empty classes that we are trying to handle. By limiting ourselves to the smallest-possible type, we reduce the chance of false positives (see next paragraph).
Note that the word "rare" appears several times in the list above. Rare implies non-zero, so we must provide a way to annotate non-bitwise moveable one-byte classes so that the IsBitwiseMoveable
trait is not deduced for them. This annotation is accomplished simply by specializing IsBitwiseMoveable
to inherit from false_type
for these rare classes.
In C++11 and later, it is possible to accurately deduce a class is bitwise moveable without relying on the one-byte heuristic. If the deduction with the one-byte heuristic yields true and the deduction without the one-byte heuristic yields false, then a static assert fires and the program is ill-formed. This error can be corrected by specializing the trait to false for the type in question.
This section illustrates intended use of this component.
Here, we use this trait in a simple algorithm called destructiveMoveArray
, which moves elements from one array to another. The algorithm is implemented using two implementation functions, one for types that are known to be bit-wise moveable, and one for other types. The first takes an extra function argument of type true_type
, the second takes and extra function argument of type false_type
:
Now we can dispatch between the two Imp functions, using the IsBitwiseMoveable
trait metafunction to determine at compile time which of the implementations should be used:
Next, to check our work, we create three classes that we will use to instantiate destructiveMoveArray
. All of the classes will log the number of constructor and destructor calls. The first class will not be decorated with the IsBitwiseMoveable
trait:
The second class is similar except that we declare it to be bit-wise moveable by specializing IsBitwiseMoveable
:
The third class is also declared to be bitwise moveable, but this time we do it using the BSLMF_NESTED_TRAIT_DECLARATION
macro:
Finally, invoke destructiveMoveArray
on arrays of all three classes:
In this example, we associate a trait not with a class, but with a class template. We create three class templates, each of which uses a different mechanisms for being associated with the IsBitwiseMoveable
trait, plus a "control" template that is not bit-wise moveable. First, we define the non-bit-wise-moveable template, NonMoveableTemplate
:
Second, we define a MoveableTemplate1
, which uses partial template specialization to associate the IsBitwiseMoveable
trait with each instantiation:
Third, we define MoveableTemplate2
, which uses the BSLMF_NESTED_TRAIT_DECLARATION
macro to associate the IsBitwiseMoveable
trait with each instantiation:
Fourth, we define MoveableTemplate3
, which is bit-wise moveable iff its t_TYPE
template parameter is bit-wise moveable. There is no way to get this effect using BSLMF_NESTED_TRAIT_DECLARATION
, so we use partial specialization combined with inheritance to "inherit" the trait from t_TYPE
:
Now, we check that the traits are correctly associated by instantiating each class with both bit-wise moveable and non-moveable types and verifying the value of IsBitwiseMoveable<T>::value
:
In this example, we define an empty class that has a non-trivial copy constructor that has a global side effect. The side effect should not be omitted, even in a destructive-move situation, so IsBitwiseMoveable
should be false. However, the heuristic described above would deduce any one-byte class (including an empty class) as bitwise-moveable by default, so we must take specific action to set the trait to false in this (rare) case.
First, we declare a normal empty class that is bitwise moveable:
The class above requires no special treatment. Next, we define an empty class that is not bitwise moveable:
Next, we specialize the IsBitwiseMoveable
trait so that NonMoveableEmptyClass
is not incorrectly flagged by trait deduction as having the IsBitwiseMoveable
trait:
Finally, we show that the first class has the IsBitwiseMoveable
trait and the second class does not:
#define BSLMF_ISBITWISEMOVEABLE_NO_SUPPORT_FOR_ARRAY_OF_UNKNOWN_BOUND 1 |