// bslmf_isbitwisemoveable.h -*-C++-*- #ifndef INCLUDED_BSLMF_ISBITWISEMOVEABLE #define INCLUDED_BSLMF_ISBITWISEMOVEABLE #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a primitive type trait for bitwise moveable classes. // //@CLASSES: // bslmf::IsBitwiseMoveable: bitwise moveable trait metafunction // //@SEE_ALSO: // //@DESCRIPTION: 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: //.. // new ((void*) p2) t_TYPE(*p1);// Or new ((void*) p2) t_TYPE(std::move(*p1)); // p1->~t_TYPE(); //.. // An object of a 't_TYPE' is *bitwise* *moveable*, if the above operation can // be replaced by the following operation without affecting correctness: //.. // std::memcpy(p2, p1, sizeof(t_TYPE)); //.. // 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). // ///What Classes are Not Bitwise Moveable? ///-------------------------------------- // A class that has any of the following attributes is !not! bitwise moveable: // //: o Its address is one of the salient attributes that comprise its value. //: //: o It contains a pointer that might (directly or indirectly) point to //: itself or to one of its own members or which stores an encoding of its //: own address or the address of one of its members. For example, a list //: implementation that includes an embedded sentinel node such that the //: last node in the list points back to the sentinel node within the list //: class object is not bitwise moveable. //: //: o Its constructor registers a pointer to itself in some static registry. //: //: o Its constructor or destructor have some side effect that, if omitted //: during destructive move, would render the program incorrect. //: //: o It contains a data member or base class that 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. // ///One-Byte Objects /// - - - - - - - - // 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: // //: o Very few types have their own address as a salient attribute. //: //: o It is not possible for an object to store a pointer to itself in only one //: byte. It is difficult to conceive of why an object would store a //: (compressed) encoding of own address in one byte. //: //: o Static registries of objects of any size are rare and are almost never a //: good design. //: //: o Constructors and destructors with side effects are also rare, and appear //: almost entirely within test code (i.e., counting constructions and //: destructions). Even in those cases, it is often acceptable or even //: preferable to skip the balanced side effects of the constructor and //: destructor during a destructive move. //: //: o Any data member or base class of a one-byte class must also be either an //: empty base class or a one-byte object, so the above rationale applies //: recursively to them. // // 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. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Using the Trait to Implement 'destructiveMoveArray' /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 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': //.. // namespace BloombergLP { // // template <class t_TYPE> // void destructiveMoveArrayImp(t_TYPE *to, // t_TYPE *from, // int size, // bsl::true_type) // { // // Bitwise moveable types can be moved using memcpy // memcpy(static_cast<void *>(to), from, size * sizeof(t_TYPE)); // } // // template <class t_TYPE> // void destructiveMoveArrayImp(t_TYPE *to, // t_TYPE *from, // int size, // bsl::false_type) // { // for (int i = 0; i < size; ++i) { // ::new(to + i) t_TYPE(from[i]); // from[i].~t_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: //.. // template <class t_TYPE> // void destructiveMoveArray(t_TYPE *to, t_TYPE *from, int size) // { // destructiveMoveArrayImp(to, from, size, // bslmf::IsBitwiseMoveable<t_TYPE>()); // } //.. // 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: //.. // class NonMoveableClass // { // private: // int d_value; // // static int d_ctorCount; // static int d_dtorCount; // // public: // static int ctorCount() { return d_ctorCount; } // static int dtorCount() { return d_dtorCount; } // // NonMoveableClass(int val = 0) : d_value(val) { ++d_ctorCount; } // NonMoveableClass(const NonMoveableClass& other) // : d_value(other.d_value) { ++d_ctorCount; } // ~NonMoveableClass() { d_dtorCount++; } // // int value() const { return d_value; } // }; // // int NonMoveableClass::d_ctorCount = 0; // int NonMoveableClass::d_dtorCount = 0; //.. // The second class is similar except that we declare it to be bit-wise // moveable by specializing 'IsBitwiseMoveable': //.. // class MoveableClass1 // { // private: // int d_value; // // static int d_ctorCount; // static int d_dtorCount; // // public: // static int ctorCount() { return d_ctorCount; } // static int dtorCount() { return d_dtorCount; } // // MoveableClass1(int val = 0) : d_value(val) { ++d_ctorCount; } // MoveableClass1(const MoveableClass1& other) // : d_value(other.d_value) { ++d_ctorCount; } // ~MoveableClass1() { d_dtorCount++; } // // int value() const { return d_value; } // }; // // int MoveableClass1::d_ctorCount = 0; // int MoveableClass1::d_dtorCount = 0; // // namespace bslmf { // template <> struct IsBitwiseMoveable<MoveableClass1> : bsl::true_type { // }; // } // close namespace bslmf //.. // The third class is also declared to be bitwise moveable, but this time we do // it using the 'BSLMF_NESTED_TRAIT_DECLARATION' macro: //.. // class MoveableClass2 // { // private: // int d_value; // // static int d_ctorCount; // static int d_dtorCount; // // public: // BSLMF_NESTED_TRAIT_DECLARATION(MoveableClass2, // bslmf::IsBitwiseMoveable); // // static int ctorCount() { return d_ctorCount; } // static int dtorCount() { return d_dtorCount; } // // MoveableClass2(int val = 0) : d_value(val) { ++d_ctorCount; } // MoveableClass2(const MoveableClass2& other) // : d_value(other.d_value) { ++d_ctorCount; } // ~MoveableClass2() { d_dtorCount++; } // // int value() const { return d_value; } // }; // // int MoveableClass2::d_ctorCount = 0; // int MoveableClass2::d_dtorCount = 0; //.. // Finally, invoke 'destructiveMoveArray' on arrays of all three classes: //.. // enum MoveableEnum { A_VALUE }; // // int usageExample1() // { // using namespace bslmf; // // // First, check the basic operation of 'IsBitwiseMoveable': // assert( IsBitwiseMoveable<int>::value); // assert( IsBitwiseMoveable<int*>::value); // assert( IsBitwiseMoveable<const int*>::value); // assert( IsBitwiseMoveable<MoveableEnum>::value); // assert(! IsBitwiseMoveable<int&>::value); // assert(! IsBitwiseMoveable<const int&>::value); // assert( IsBitwiseMoveable<MoveableClass1>::value); // assert( IsBitwiseMoveable<const MoveableClass1>::value); // assert( IsBitwiseMoveable<MoveableClass2>::value); // assert( IsBitwiseMoveable<volatile MoveableClass2>::value); // assert(! IsBitwiseMoveable<NonMoveableClass>::value); // assert(! IsBitwiseMoveable<const NonMoveableClass>::value); // // // For each of our test classes, allocate an array, construct three // // objects into it, then move it into another array. // const int nObj = 3; // // { // NonMoveableClass *p1 = (NonMoveableClass*) // ::operator new(nObj * sizeof(NonMoveableClass)); // NonMoveableClass *p2 = (NonMoveableClass*) // ::operator new(nObj * sizeof(NonMoveableClass)); // // for (int i = 0; i < nObj; ++i) { // new(p1 + i) NonMoveableClass(i); // } // // assert(nObj == NonMoveableClass::ctorCount()); // assert(0 == NonMoveableClass::dtorCount()); // // assert(! IsBitwiseMoveable<NonMoveableClass>::value); // destructiveMoveArray(p2, p1, nObj); // // // Verify that constructor and destructor were called on each move // assert(2 * nObj == NonMoveableClass::ctorCount()); // assert(nObj == NonMoveableClass::dtorCount()); // // // Verify contents // for (int i = 0; i < nObj; ++i) { // assert(i == p2[i].value()); // } // // // Destroy and deallocate // for (int i = 0; i < nObj; ++i) { // p2[i].~NonMoveableClass(); // } // ::operator delete(p1); // ::operator delete(p2); // } // // { // MoveableClass1 *p1 = (MoveableClass1*) // ::operator new(nObj * sizeof(MoveableClass1)); // MoveableClass1 *p2 = (MoveableClass1*) // ::operator new(nObj * sizeof(MoveableClass1)); // // for (int i = 0; i < nObj; ++i) { // ::new(p1 + i) MoveableClass1(i); // } // // assert(nObj == MoveableClass1::ctorCount()); // assert(0 == MoveableClass1::dtorCount()); // // assert(IsBitwiseMoveable<MoveableClass1>::value); // destructiveMoveArray(p2, p1, nObj); // // // Verify that constructor and destructor were NOT called on each // // move // assert(nObj == MoveableClass1::ctorCount()); // assert(0 == MoveableClass1::dtorCount()); // // // Verify contents // for (int i = 0; i < nObj; ++i) { // assert(i == p2[i].value()); // } // // // Destroy and deallocate // for (int i = 0; i < nObj; ++i) { // p2[i].~MoveableClass1(); // } // ::operator delete(p1); // ::operator delete(p2); // } // // { // MoveableClass2 *p1 = (MoveableClass2*) // ::operator new(nObj * sizeof(MoveableClass2)); // MoveableClass2 *p2 = (MoveableClass2*) // ::operator new(nObj * sizeof(MoveableClass2)); // // for (int i = 0; i < nObj; ++i) { // ::new(p1 + i) MoveableClass2(i); // } // // assert(nObj == MoveableClass2::ctorCount()); // assert(0 == MoveableClass2::dtorCount()); // // assert(IsBitwiseMoveable<MoveableClass2>::value); // destructiveMoveArray(p2, p1, nObj); // // // Verify that constructor and destructor were NOT called on each // // move // assert(nObj == MoveableClass2::ctorCount()); // assert(0 == MoveableClass2::dtorCount()); // // // Verify contents // for (int i = 0; i < nObj; ++i) { // assert(i == p2[i].value()); // } // // // Destroy and deallocate // for (int i = 0; i < nObj; ++i) { // p2[i].~MoveableClass2(); // } // ::operator delete(p1); // ::operator delete(p2); // } // // return 0; // } // // } // close enterprise namespace //.. // ///Example 2: Associating a Trait with a Class Template /// - - - - - - - - - - - - - - - - - - - - - - - - - - // 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': //.. // namespace BloombergLP { // // template <class t_TYPE> // struct NonMoveableTemplate // { // t_TYPE d_p; // }; //.. // Second, we define a 'MoveableTemplate1', which uses partial template // specialization to associate the 'IsBitwiseMoveable' trait with each // instantiation: //.. // template <class t_TYPE> // struct MoveableTemplate1 // { // t_TYPE *d_p; // }; // // namespace bslmf { // template <class t_TYPE> // struct IsBitwiseMoveable<MoveableTemplate1<t_TYPE> > : bsl::true_type { // }; // } // close namespace bslmf //.. // Third, we define 'MoveableTemplate2', which uses the // 'BSLMF_NESTED_TRAIT_DECLARATION' macro to associate the 'IsBitwiseMoveable' // trait with each instantiation: //.. // template <class t_TYPE> // struct MoveableTemplate2 // { // t_TYPE *d_p; // // BSLMF_NESTED_TRAIT_DECLARATION(MoveableTemplate2, // bslmf::IsBitwiseMoveable); // }; //.. // 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': //.. // template <class t_TYPE> // struct MoveableTemplate3 // { // t_TYPE d_p; // }; // // namespace bslmf { // template <class t_TYPE> // struct IsBitwiseMoveable<MoveableTemplate3<t_TYPE> > : // IsBitwiseMoveable<t_TYPE>::type { }; // } // close namespace bslmf //.. // 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': //.. // int usageExample2() // { // using namespace bslmf; // // assert(! IsBitwiseMoveable< // NonMoveableTemplate<NonMoveableClass> >::value); // assert(! IsBitwiseMoveable< // NonMoveableTemplate<MoveableClass1> >::value); // // assert( IsBitwiseMoveable< // MoveableTemplate1<NonMoveableClass> >::value); // assert( IsBitwiseMoveable< // MoveableTemplate1<MoveableClass1> >::value); // // assert( IsBitwiseMoveable< // MoveableTemplate2<NonMoveableClass> >::value); // assert( IsBitwiseMoveable< // MoveableTemplate2<MoveableClass1> >::value); // // assert(! IsBitwiseMoveable< // MoveableTemplate3<NonMoveableClass> >::value); // assert( IsBitwiseMoveable< // MoveableTemplate3<MoveableClass1> >::value); // // return 0; // } // // } // close enterprise namespace //.. // ///Example 3: Avoiding False Positives on One-Byte Classes ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - // 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: //.. // namespace BloombergLP { // namespace xyza { // // class MoveableEmptyClass // { // // This class is implicitly moveable by virtue of being only one byte // // in size. // }; //.. // The class above requires no special treatment. Next, we define an empty // class that is not bitwise moveable: //.. // class NonMoveableEmptyClass // { // // This class is empty, which normally would imply bitwise moveability. // // However, because it has a non-trivial move/copy constructor, it // // should not be bitwise moved. // // static int d_count; // // public: // NonMoveableEmptyClass() { ++d_count; } // NonMoveableEmptyClass(const NonMoveableEmptyClass&) { ++d_count; } // }; // // int NonMoveableEmptyClass::d_count = 0; // // } // close package namespace //.. // Next, we specialize the 'IsBitwiseMoveable' trait so that // 'NonMoveableEmptyClass' is not incorrectly flagged by trait deduction as // having the 'IsBitwiseMoveable' trait: //.. // namespace bslmf { // // template <> // struct IsBitwiseMoveable<xyza::NonMoveableEmptyClass> : bsl::false_type // { // }; // // } // close namespace bslmf //.. // Finally, we show that the first class has the 'IsBitwiseMoveable' trait and // the second class does not: //.. // int main() // { // using namespace bslmf; // // assert( IsBitwiseMoveable<xyza::MoveableEmptyClass>::value); // assert(! IsBitwiseMoveable<xyza::NonMoveableEmptyClass>::value); // } // // } // close enterprise namespace //.. #include <bslscm_version.h> #include <bslmf_conditional.h> #include <bslmf_detectnestedtrait.h> #include <bslmf_integralconstant.h> #include <bslmf_isempty.h> #include <bslmf_isfunction.h> #include <bslmf_isreference.h> #include <bslmf_istriviallycopyable.h> #include <bsls_compilerfeatures.h> #include <bsls_platform.h> #include <stddef.h> #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER # include <type_traits> #endif // BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bsls_nativestd.h> #endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #if !defined(BSLS_PLATFORM_CMP_IBM) // Last checked with the xlC 12.1 compiler. The IBM xlC compiler has problems // correctly handling arrays of unknown bound as template parameters. # define BSLMF_ISBITWISEMOVEABLE_NO_SUPPORT_FOR_ARRAY_OF_UNKNOWN_BOUND 1 #endif namespace BloombergLP { namespace bslmf { template <class t_TYPE> struct IsBitwiseMoveable; template <class t_TYPE, bool = bsl::is_reference<t_TYPE>::value || bsl::is_function<t_TYPE>::value> struct IsBitwiseMoveable_Imp : bsl::false_type { // Function types and reference types are not object types, and so are not // bitwise-movable. Both categories of types must be explicitly handled by // a distinct template specialization to avoid attempting to instantiate // invalid code, such as 'sizeof(FUNCTION_TYPE)', when computing the trait // result for other (object) types. }; template <class t_TYPE> struct IsBitwiseMoveable_Imp<t_TYPE, false> { // Core implementation of the 'IsBitwiseMoveable' trait. A class is // detected as being bitwise moveable iff it is trivially copyable or it // has a nested trait declaration for the 'IsBitwiseMoveable' trait. In // C++03 however, detection of trivially copyable classes is imperfect and // depends on programmer intervention. As many empty classes (including // standard classes like 'std::less<T>' would not be detected as being // trivially copyable and, therefore, bitwise moveable, a heuristic is put // in place whereby any type of one byte size is assumed to be bitwise // moveable. See component-level documentation for this component for more // details on this heuristic and how to avoid false positives. private: static const bool k_NestedBitwiseMoveableTrait = DetectNestedTrait<t_TYPE, IsBitwiseMoveable>::value; public: static const bool value = bsl::is_trivially_copyable<t_TYPE>::value || k_NestedBitwiseMoveableTrait || sizeof(t_TYPE) == 1; typedef bsl::integral_constant<bool, value> type; #if defined(BSLS_COMPILERFEATURES_SUPPORT_STATIC_ASSERT) && \ defined(BSLMF_ISTRIVIALLYCOPYABLE_NATIVE_IMPLEMENTATION) // In C++11 and beyond, we can accurately detect trivial-copiable types // which would allow us to remove the one-byte heuristic used above. // Testing with gcc-5 on Bloomberg production software indicates that there // are many 1-byte types, which are conceptually bitwise moveable, that are // not correctly marked bitwise moveable (for example, any 1-byte // code-generated type). For the moment we have decided not to enable more // conservative logic for automatically deducing the // 'IsBitwiseMoveableTrait'. The more conservative logic is shown below // for future reference: static_assert(!bsl::is_reference<t_TYPE>::value, "This imp-detail instantiation should not be selected for " "reference types"); static_assert(!bsl::is_function<t_TYPE>::value, "This imp-detail instantiation should not be selected for " "function types"); static const bool k_ValueWithoutOnebyteHeuristic = bsl::is_trivially_copyable<t_TYPE>::value || std::is_empty<t_TYPE>::value // required for gcc < 5.0 || k_NestedBitwiseMoveableTrait; #endif }; // ======================== // struct IsBitwiseMoveable // ======================== template <class t_TYPE> struct IsBitwiseMoveable : IsBitwiseMoveable_Imp<t_TYPE>::type { // Trait metafunction that determines whether the specified parameter // 't_TYPE' is bitwise moveable. If 'IsBitwiseMoveable<t_TYPE>' is derived // from 'bsl::true_type' then 't_TYPE' is bitwise moveable. Otherwise, // bitwise moveability cannot be inferred for 't_TYPE'. This trait can be // associated with a bitwise moveable user-defined class by specializing // this class or by using the 'BSLMF_NESTED_TRAIT_DECLARATION' macro. }; template <> struct IsBitwiseMoveable<void> : bsl::false_type { // Traits metafunction explicit specialization to indicate that the type // 'void' is not bitwise movable. Note that this specialization is used // as a simpler option than making the compile-time logic of the // 'IsBitwiseMoveable_Imp' template robust for 'void' types. }; template <class t_TYPE> struct IsBitwiseMoveable<const t_TYPE> : IsBitwiseMoveable<t_TYPE>::type { // Trait metafunction that determines whether the specified parameter // 't_TYPE' is bitwise moveable by stripping off the 'const' qualifier and // forwarding to the base-case of 'IsBitwiseMoveable'. }; template <class t_TYPE> struct IsBitwiseMoveable<volatile t_TYPE> : IsBitwiseMoveable<t_TYPE>::type { // Trait metafunction that determines whether the specified parameter // 't_TYPE' is bitwise moveable by stripping off the 'volatile' qualifier // and forwarding to the base-case of 'IsBitwiseMoveable'. }; template <class t_TYPE> struct IsBitwiseMoveable<const volatile t_TYPE> : IsBitwiseMoveable<t_TYPE>::type { // Trait metafunction that determines whether the specified parameter // 't_TYPE' is bitwise moveable by stripping off the 'const' and 'volatile' // qualifiers and forwarding to the base-case of 'IsBitwiseMoveable'. }; template <class t_TYPE, size_t t_LEN> struct IsBitwiseMoveable<t_TYPE[t_LEN]> : IsBitwiseMoveable<t_TYPE>::type { }; template <class t_TYPE, size_t t_LEN> struct IsBitwiseMoveable<const t_TYPE[t_LEN]> : IsBitwiseMoveable<t_TYPE>::type { }; template <class t_TYPE, size_t t_LEN> struct IsBitwiseMoveable<volatile t_TYPE[t_LEN]> : IsBitwiseMoveable<t_TYPE>::type { }; template <class t_TYPE, size_t t_LEN> struct IsBitwiseMoveable<const volatile t_TYPE[t_LEN]> : IsBitwiseMoveable<t_TYPE>::type { }; #if !defined(BSLMF_ISBITWISEMOVEABLE_NO_SUPPORT_FOR_ARRAY_OF_UNKNOWN_BOUND) template <class t_TYPE> struct IsBitwiseMoveable<t_TYPE[]> : IsBitwiseMoveable<t_TYPE>::type { }; template <class t_TYPE> struct IsBitwiseMoveable<const t_TYPE[]> : IsBitwiseMoveable<t_TYPE>::type { }; template <class t_TYPE> struct IsBitwiseMoveable<volatile t_TYPE[]> : IsBitwiseMoveable<t_TYPE>::type { }; template <class t_TYPE> struct IsBitwiseMoveable<const volatile t_TYPE[]> : IsBitwiseMoveable<t_TYPE>::type { }; #endif // BSLMF_ISBITWISEMOVEABLE_NO_SUPPORT_FOR_ARRAY_OF_UNKNOWN_BOUND } // close package namespace } // close enterprise namespace #endif // ! defined(INCLUDED_BSLMF_ISBITWISEMOVEABLE) // ---------------------------------------------------------------------------- // Copyright 2013 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------