Chapter 11. Type Traits

I am the voice of today, the herald of tomorrow…. I am the leaden army that conquers the world—I am type.

The Type Speaks
FREDERIC WILLIAM GOUDY

The TR1 library has more than 50 class templates that provide compile-time constants based on various properties of their type arguments or new types that are like their type argument with some of its properties changed. They are basic building blocks. You can use them directly in your code, but more often, you’ll use them to construct other templates that do something you need in a more narrowly focused library or in your current application.

As an example, the C++ language allows you to overload functions based on their argument types.[1] In some cases, though, you need a more flexible way to decide which of several functions to call. In Chapter 13, for instance, we’ll look at a template member function named seed that does one thing if it is called with a value of an integral type and something else if it is called with a function object. One way to handle this overloading would be to write a separate overloaded function for each of the nine standard integral types and a function template to handle all other argument types:

Example 11.1. Dispatch with Overloading (typetraits/seedover.cpp)



# include <iostream>
# include <typeinfo>
# include <random>
using std :: cout;

template <class Ty>
void seed_integral (Ty val)
  { // seed with an integral type

  
  cout << "Called integral version of seed,"
       << " with argument type "
       << typeid(Ty).name() << ' ';
  }

template <class Ty>
void seed_object(Ty val)
  { // seed with a nonintegral type
  cout << "Called nonintegral version of seed,"
       << " with argument type "
       << typeid(Ty).name() << '   ' ;
  }

    // dispatch to seed_integral for integral types
void seed(char val) { seed_integral(val); }
void seed(unsigned char val) { seed_integral(val); }
void seed(signed char val) { seed_integral(val); }
void seed(short val) { seed_integral(val); }
void seed(unsigned short val) { seed_integral(val); }
void seed(int val) { seed_integral(val); }
void seed(unsigned int val) { seed_integral(val); }
void seed(long val) { seed_integral(val); }
void seed(unsigned long val) { seed_integral(val); }

    // dispatch to seed_object for nonintegral types
template <class Ty>
void seed(Ty val) { seed_object(val); }

int main()
  { // call seed with several argument types
  seed(1);
  seed('a'),
  seed(std ::tr1 :: mt19937 ());
  return 0;
  }


However, if your compiler provides the C99 integral types long long and unsigned long long, this code is wrong: It will call seed_object instead of seed_integral when called with an argument of either of those types. The C++ standard doesn’t require these types, though, so if you want to make this code robust, you need to provide overloads for long long and unsigned long long only when you’re using a compiler that supports them. Since there’s no standard way of identifying those compilers, you have to write additional code to somehow indicate whether to provide these overloads. This kind of problem is more easily solved in a compiler-specific library than on the fly in your code, because the library writer knows the details of the compiler that the library targets. With the addition of a library class template named is_integral, the same problem can be solved with much less repetition.[2]

Example 11.2. Dispatch with Type Traits (typetraits/seedtrait.cpp)


#include <type_traits>
#include <iostream>
#include <typeinfo>
#include <random>
using std::tr1::is_integral;
using std::tr1::true_type; using std::tr1::false_type;
using std::cout;

template <class Ty>
void seed_impl(Ty val, const true_type&)
  { // seed with an integral type
  cout << "Called integral version of seed,"
  cout << " with argument type "
       << typeid(Ty).name() << ' ';
  }

template <class Ty>
void seed_impl(Ty val, const false_type&)
  { // seed with a non-integral type
  cout << "Called non-integral version of seed,"
  cout << " with argument type "
       << typeid(Ty).name() << ' ';
  }

template <class Ty>
void seed(Ty val)
  { // dispatch to appropriate version of seed_impl
  seed_impl(val, is_integral <Ty>());
  }

int main()

  
  { // call seed with several argument types
  seed(1);
  seed('a'),
  seed(std::tr1::mt19937());
  return 0;
  }


This code works because an instantiation of std::tr1::is_integral for an integral type is derived from the type std::tr1::true_type, and an instantiation for any other type is derived from the type std::tr1::false_-type. When it calls seed_impl, seed passes an object is_integral<Ty>(), and the compiler chooses the version of seed_impl whose second argument matches the base type of that object.

11.1. The Header <type_traits>

The header <type_traits> provides a class template named integral_constant that holds a compile-time constant value supplied as one of its template arguments. The header also provides two typedefs—true_type and false_-type—that are specializations of integral_constant holding the Boolean values true and false, respectively.

The header also provides 53 class templates that deal in various ways with their type arguments. These templates are broadly categorized as type predicates, type queries, and type transformations. A type predicate is derived from the type true_type if the condition that it tests is true or from false_type if the condition is false. Most of the type predicates in the TR1 library take a single type argument and give you information about that type. Some take two type arguments and give you information about the relationship between the two types. A type query is derived from a specialization of integral_constant and provides the numeric value of the property being queried. A type transformation has a nested typedef that is a synonym for the type passed as the type argument to the template, modified according to the particular transformation.

The TR1 library specification defines some technical terms that describe the interface to various kinds of type traits. These terms are shorthand for a set of properties and can make it easier to describe the requirements for code that uses various type traits.[3]

A UnaryTypeTrait is a class template that takes one template type argument and, if necessary, additional arguments that more fully define the property being described. It must be default constructible and must be derived, directly or indirectly, from a specialization of integral_constant. In the TR1 library, each of the type predicates that takes one argument and each of the type queries meet the requirements for a UnaryTypeTrait.

A BinaryTypeTrait is just like a UnaryTypeTrait but takes two type arguments. In the TR1 library, each of the type predicates that take two arguments meets the requirements for a BinaryTypeTrait.

A TransformationTypeTrait is a class template that takes one template type argument and, if necessary, additional arguments that more fully define the modification. The class template defines a nested type named type that is a synonym for the modified type. In the TR1 library, each of the type transformations satisfies the requirements for a TransformationTypeTrait.

Some of the specifications include a paragraph beginning with the words “An implementation may …”. This phrase introduces a compromise rule, imposed by the need to implement the TR1 library without help from the compiler. Some of the type predicates can’t be written without such help, so the specification allows them to give incorrect information in certain specific ways. These compromise rules have been carefully formulated, so that in most cases, the incorrect information won’t affect the correctness of your code.[4] Be sure, though, that you understand the weaker form of the predicate before you use it.

namespace std {
 namespace tr1 {

     // HELPER TYPES (Section 11.2)
template < class Ty, Ty val > struct integral_constant;
typedef integral_constant < bool, false > false_type ;
typedef integral_constant < bool, true > true_type;

     // PRIMARY TYPE CATEGORIES (Section 11.3)
template < class Ty > struct is_void ;
template < class Ty > struct is_integral ;
template < class Ty > struct is_floating_point ;
template < class Ty > struct is_array ;

template < class Ty > struct is_pointer;
template < class Ty > struct is_reference;
template < class Ty > struct is_member_object_pointer;

template < class Ty > struct is_member_function_pointer;
template < class Ty > struct is_enum;
template < class Ty > struct is_union;
template < class Ty > struct is_class;
template < class Ty > struct is_function;

    // COMPOSITE TYPE CATEGORIES (Section 11.4)
template < class Ty > struct is_arithmetic;
template < class Ty > struct is_fundamental;
template < class Ty > struct is_object;
template < class Ty > struct is_scalar;
template < class Ty > struct is_compound;
template < class Ty > struct is_member_pointer;

     // TYPE PROPERTIES (Section 11.5)
template < class Ty > struct is_const;
template < class Ty > struct is_volatile;
template < class Ty > struct is_pod;
template < class Ty > struct is_empty;
template < class Ty > struct is_polymorphic;
template < class Ty > struct is_abstract;
template < class Ty > struct has_trivial_constructor;
template < class Ty > struct has_trivial_copy;
template < class Ty > struct has_trivial_assign;
template < class Ty > struct has_trivial_destructor;
template < class Ty > struct has_nothrow_constructor;
template < class Ty > struct has_nothrow_copy;
template < class Ty > struct has_nothrow_assign;
template < class Ty > struct has_virtual_destructor;
template < class Ty > struct is_signed;
template < class Ty > struct is_unsigned;
template < class Ty > struct rank;
template < class Ty, unsigned I = 0 > struct extent;

     // TYPE RELATIONSHIPS (Section 11.6)
template < class Ty1, class Ty2 > struct is_same;
template < class From, class To > struct is_convertible;
template < class Base, class Derived > struct is_base_of;

     // TYPE TRANSFORMATIONS (Section 11.7)
template < class Ty > struct remove_const;

template < class Ty > struct remove_volatile;
template < class Ty > struct remove_cv;
template < class Ty > struct add_const;
template < class Ty > struct add_volatile;
template < class Ty > struct add_cv;
template < class Ty > struct remove_reference;
template < class Ty > struct add_reference;
template < class Ty > struct remove_pointer;
template < class Ty > struct add_pointer;
template < class Ty > struct remove_extent;
template < class Ty > struct remove_all_extents;

     // ALIGNMENT (Section 11.8)
template < class Ty > struct alignment_of;
template < size_t Len, size_t Align > struct aligned_storage;

} };

11.2. Helper Types

template < class Ty, Ty val > struct integral_constant {
  static const Ty value = val ;
  typedef Ty value_type ;
  typedef integral_constant <Ty, val > type ;
};
typedef integral_constant < bool, true > true_type ;
typedef integral_constant < bool, false > false_type ;

The static member value is an integral constant expression whose value is the template argument val.

The type integral_constant<Ty, val>::value_type is a synonym for the template type argument Ty.

The type integral_constant<Ty, val>::type is a synonym for the type of the template instantiation, integral_constant<Ty, val>.

The type true_type is a synonym for template specialization integral_-constant<bool, true>.

The type false_type is a synonym for the template specialization integral_constant<bool, false>.

The class template integral_constant holds a value that is an integral constant expression. Integral constant expressions are often called compile-time constants because they can be used to provide values, such as the number of elements in an array declaration, the number of bits in a bitfield, or the value of a case label, that must be known at compile time. In addition, because the stored value is passed as a template argument, template instantiations that hold different values are different types. This makes it possible to choose among overloaded functions based on values, as we saw in the previous example.

The two typedefs true_type and false_type are integral_constant specializations that hold Boolean values. They provide base classes for the unary type traits and the binary type traits, each of which tells you whether a type or a pair of types has a certain property.

11.3. Primary Type Categories

Each of these class templates defines a type predicate. The templates remove any top-level const or volatile qualifiers from their type argument Ty and then determine whether the resulting type belongs to the template’s type category. These type categories are defined in the C++ Standard in Section 3.9 [basic.types]. The categories are complete and mutually exclusive: for any type Ty, exactly one of these type predicates will be true.

template < class Ty > struct is_void;

The template specialization is_void<Ty> is derived from true_type if the type Ty is a cv-qualified form of void. Otherwise, it is derived from false_type.

template < class Ty > struct is_integral;

The template specialization is_integral<Ty> is derived from true_type if the type Ty is a cv-qualified form of an integral type. Otherwise, it is derived from false_type.

An integral type is one of char, unsigned char, signed char, short, unsigned short, int, unsigned int, long, and unsigned long. If a compiler supports them, the C99 types long long and unsigned long long are also integral types.

template < class Ty > struct is_floating_point;

The template specialization is_floating_point<Ty> is derived from true_type if the type Ty is a cv-qualified form of a floating-point type. Otherwise, it is derived from false_type.

A floating-point type is one of float, double, and long double.

template < class Ty > struct is_array;

The template specialization is_array<Ty> is derived from true_type if the type Ty is a cv-qualified form of an array type. Otherwise, it is derived from false_type.

The class template array (see Chapter 4) is not an array type.

template < class Ty > struct is_pointer;

The template specialization is_pointer<Ty> is derived from true_type if the type Ty is a cv-qualified form of a pointer type. Otherwise, it is derived from false_type.

This predicate is true for ordinary pointer types but not for pointers to members.

template < class Ty > struct is_reference;

The template specialization is_reference<Ty> is derived from true_-type if the type Ty is a cv-qualified form of a reference type. Otherwise, it is derived from false_type.

template < class Ty > struct is_member_object_pointer;

The template specialization is_member_object_pointer<Ty> is derived from true_type if the type Ty is a cv-qualified form of pointer to data member. Otherwise, it is derived from false_type.

template < class Ty > struct is_member_function_pointer;

The template specialization is_member_function_pointer<Ty> is derived from true_type if the type Ty is a cv-qualified form of pointer to member function. Otherwise, it is derived from false_type.

template < class Ty > struct is_enum;

The template specialization is_enum<Ty> is derived from true_type if the type Ty is a cv-qualified form of an enumeration type. Otherwise, it is derived from false_type.

template < class Ty > struct is_union;

The template specialization is_union<Ty> is derived from true_type if the type Ty is a cv-qualified form of a union type. Otherwise, it is derived from false_type.

An implementation may define this template as template <class Ty> struct is_union {}.

This template’s compromise rule means that code that uses is_union to determine whether a type is a union type—by looking for one of the base types true_type or false_type—will produce a diagnostic if is_union is not supported.

template < class Ty > struct is_class;

The template specialization is_class<Ty> is derived from true_type if the type Ty is a cv-qualified form of a class type but not a union type. Otherwise, it is derived from false_type.

An implementation may define this template as template <class Ty> struct is_class {}.

This template’s compromise rule means that code that uses is_class to determine whether a type is a class type—by looking for one of the base types true_type or false_type—will produce a diagnostic if is_class is not supported.

template < class Ty > struct is_function;

The template specialization is_function<Ty> is derived from true_type if the type Ty is a cv-qualified form of a function type. Otherwise, it is derived from false_type.

Note that this predicate detects function types, not pointers to functions. As we saw in Chapter 9, a function type is a return type followed by a left parenthesis followed by a possibly empty argument list followed by a right parenthesis.

11.4. Composite Type Categories

Each of these class templates defines a type predicate. The templates remove any top-level const or volatile qualifiers from their type argument Ty and then determine whether the resulting type belongs to the template’s type category. Defined in various places in the C++ standard, these type categories are referred to as composite because they can be composed from the primary predicates discussed in Section 11.3.

template < class Ty > struct is_arithmetic;

The template specialization is_arithmetic<Ty> is derived from true_-type if the type Ty is a cv-qualified form of an arithmetic type. Otherwise, it is derived from false_type.

An arithmetic type is an integral type or a floating-point type.

template < class Ty > struct is_fundamental;

The template specialization is_fundamental<Ty> is derived from true_-type if the type Ty is a cv-qualified form of a fundamental type. Otherwise, it is derived from false_type.

A fundamental type is an integral type, a floating-point type, or the type void.

template < class Ty > struct is_object;

The template specialization is_object<Ty> is derived from true_type if the type Ty is a cv-qualified form of an object type. Otherwise, it is derived from false_type.

An object type is any type that is not a function type, not a reference type, and not void.

template < class Ty > struct is_scalar;

The template specialization is_scalar<Ty> is derived from true_type if the type Ty is a cv-qualified form of a scalar type. Otherwise, it is derived from false_type.

A scalar type is an arithmetic type, an enumeration type, a pointer type, or a pointer to member type.

template < class Ty > struct is_compound;

The template specialization is_compound<Ty> is derived from true_type if the type Ty is a cv-qualified form of a compound type. Otherwise, it is derived from false_type.

A compound type is a type that is not a fundamental type.

template < class Ty > struct is_member_pointer;

The template specialization is_member_pointer<Ty> is derived from true_type if the type Ty is a cv-qualified form of a pointer to member. Otherwise, it is derived from false_type.

A member pointer is a pointer to member function or a pointer to member data.

11.5. Type Properties

The class templates rank and extent define type queries. All the other class templates define type predicates. They provide information about properties of their type argument Ty.

template < class Ty > struct is_const;

The template specialization is_const<Ty> is derived from true_type if the type Ty has a top-level const qualifier. Otherwise, it is derived from false_type.

template < class Ty > struct is_volatile;

The template specialization is_volatile<Ty> is derived from true_type if the type Ty has a top-level volatile qualifier. Otherwise, it is derived from false_type.

template < class Ty > struct is_pod;

The type Ty must be a complete type.

The template specialization is_pod<Ty> is derived from true_type if the type Ty is a POD[5] type. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_-type or true_type without regard to the properties of the type argument Ty, except that the following assertions shall be true:

is_pod <Ty>:: value  >=
  (is_scalar <Ty>:: value ||  is_void <Ty>:: value)
is_pod <Ty >:: value  == is_pod <const Ty>:: value
is_pod <Ty >:: value  == is_pod <volatile Ty>:: value
is_pod <Ty >:: value  ==
  is_pod < remove_extent <Ty>:: type>:: value

Generally speaking, a POD is any data type whose bits alone determine its value. In particular, POD types can be copied with memcpy, so when you need to copy an array of objects of POD type, you can use memcpy on the entire array rather than assigning elements one at a time.

This template’s compromise rule means that all cv-qualified forms[6] of all scalar types (and the type void) are PODs and that arrays of PODs are also PODs. Thus, when it tells you that a type is a POD, it will be correct; when it tells you that a type is not a POD, it will sometimes be wrong.

template < class Ty > struct is_empty;

The type Ty must be a complete type.

The template specialization is_empty<Ty> is derived from true_type if the type Ty is an empty type. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_type or true_type without regard to the properties of the type argument Ty.

This template’s compromise rule implies that you cannot use is_empty in portable code. Although that’s technically true, the intention was to allow is_empty<Ty> to be derived from false_type even if the type Ty is empty but not to allow it to be derived from true_type if Ty is not empty. Under this rule, is_empty<Ty>::value can be used to trigger optimizations that depend on a type being empty. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct is_polymorphic;

The type Ty must be a complete type.

The template specialization is_polymorphic<Ty> is derived from true_-type if the type Ty is a polymorphic type. Otherwise, it is derived from false_type.

An implementation may define this template as template <class Ty> struct is_polymorphic {}.

A polymorphic type is a class type with at least one virtual function.

This template’s compromise rule means that code that uses is_polymorphic to determine whether a type is polymorphic—by looking for one of the base types true_type or false_type—will produce a diagnostic if is_polymorphic is not supported.

template < class Ty > struct is_abstract;

The type Ty must be a complete type.

The template specialization is_abstract<Ty> is derived from true_type if the type Ty is an abstract type. Otherwise, it is derived from false_-type.

An implementation may define this template as template <class Ty> struct is_abstract {}.

An abstract class is one with at least one pure virtual function. Abstract classes are used to define the interface to a family of types defined by derived classes that override virtual functions, both ordinary and pure, in the abstract base type.

This template’s compromise rule means that code that uses is_abstract to determine whether a class is abstract—by looking for one of the base types true_type or false_type—will produce a diagnostic if is_abstract is not supported.

template < class Ty > struct has_trivial_constructor;

The type Ty must be a complete type.

The template specialization has_trivial_constructor<Ty> is derived from true_type if the default constructor for the type Ty is trivial. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_-type or true_type without regard to the properties of the type argument Ty, except that the following assertions shall be true:

has_trivial_constructor <Ty>:: value >=
  is_pod <Ty >:: value
has_trivial_constructor <Ty>:: value ==
  has_trivial_constructor <remove_extent <Ty>:: type>
    :: value

A trivial default constructor is an implicitly declared default constructor for a class that has no virtual functions and no virtual base classes, whose direct base classes all have trivial default constructors, and whose non-static data members of class type all have trivial default constructors. Classes with trivial default constructors don’t require initialization. Classes that do not have trivial default constructors can’t be used as members of unions.

This template’s compromise rule implies that you cannot use has_trivial_constructor in portable code. Although that’s technically true, the intention was to allow implementations to derived has_trivial_constructor<Ty> from false_type even if the type Ty has a trivial constructor—except in the special cases enumerated in the compromise rule—but not to allow it to be derived from true_type if Ty does not have a trivial constructor. Under this rule, has_trivial_constructor<Ty>::value can be used to trigger optimizations that depend on a type having a trivial constructor. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_trivial_copy;

The type Ty must be a complete type.

The template specialization has_trivial_copy<Ty> is derived from the class true_type if the copy constructor for the type Ty is trivial. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_-type or true_type without regard to the properties of the type argument Ty, except that the following assertions shall be true:

has_trivial_copy <Ty>:: value>= is_pod <Ty>:: value
has_trivial_copy <Ty>:: value ==
  has_trivial_copy < remove_extent <Ty>:: type>:: value

A trivial copy constructor is an implicitly declared copy constructor for a class that has no virtual functions and no virtual base classes, whose direct base classes all have trivial copy constructors, and whose non-static data members of class type all have trivial copy constructors. Classes that have trivial copy constructors can be copy constructed by copying their bits, typically with memcpy. Classes that do not have trivial copy constructors can’t be used as members of unions.

This template’s compromise rule implies that you cannot use has_trivial_copy in portable code. Although that’s technically true, the intention was to allow has_trivial_copy<Ty> to be derived from false_type even if the type Ty has a trivial copy constructor—except in the special cases enumerated in the compromise rule—but not to allow it to be derived from true_type if Ty does not have a trivial copy constructor. Under this rule, has_trivial_-copy<Ty>::value can be used to trigger optimizations that depend on a type having a trivial copy constructor. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_trivial_assign;

The type Ty must be a complete type.

The template specialization has_trivial_assign<Ty> is derived from true_type if the assignment for the type Ty is trivial. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_-type or true_type without regard to the properties of the type argument Ty, except that the following assertions shall be true:

has_trivial_assign <Ty>:: value>= is_pod <Ty>:: value
has_trivial_assign <Ty>:: value ==
  has_trivial_assign <remove_extent <Ty>:: type>:: value

A trivial copy assignment operator is an implicitly declared copy assignment operator for a class that has no virtual functions and no virtual base classes, whose direct base classes all have trivial copy assignment operators, and whose non-static data members of class type all have trivial copy assignment operators. Classes that have trivial copy assignment operators can be copied by copying their bits, typically with memcpy. Classes that do not have trivial copy assignment operators can’t be used as members of unions.

This template’s compromise rule implies that you cannot use has_trivial_assign in portable code. Although that’s technically true, the intention was to allow has_trivial_assign<Ty> to be derived from false_type even if the type Ty has a trivial copy assignment operator—except in the special cases enumerated in the compromise rule—but not to allow it to be derived from true_type if Ty does not have a trivial copy assignment operator. Under this rule, has_trivial_assign<Ty>::value can be used to trigger optimizations that depend on a type having a trivial copy assignment operator. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_trivial_destructor;

The type Ty must be a complete type.

The template specialization has_trivial_destructor<Ty > is derived from true_type if the destructor for the type Ty is trivial. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_-type or true_type without regard to the properties of the type argument Ty, except that the following assertions shall be true:

has_trivial_destructor <Ty>:: value>= is_pod <Ty>:: value
has_trivial_destructor <Ty>:: value ==
  has_trivial_destructor <remove_extent <Ty>:: type>
    :: value

A trivial destructor is an implicitly declared destructor for a class whose direct base classes all have trivial destructors and whose non-static data members of class type all have trivial destructors. Classes that have trivial destructors do not need to be destroyed. Classes that do not have trivial destructors can’t be used as members of unions.

This template’s compromise rule implies that you cannot use has_trivial_destructor in portable code. Although that’s technically true, the intention was to allow has_trivial_destructor<Ty> to be derived from false_-type even if the type Ty has a trivial destructor—except in the special cases enumerated in the compromise rule—but not to allow it to be derived from true_type if Ty does not have a trivial destructor. Under this rule, has_-trivial_destructor<Ty>::value can be used to trigger optimizations that depend on a type having a trivial destructor. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_nothrow_constructor;

The type Ty must be a complete type.

The template specialization has_nothrow_constructor<Ty> is derived from true_type if the default constructor for the type Ty has a nothrow specifier or if the compiler can otherwise determine that it will not throw an exception. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_type or true_type without regard to the properties of the type argument Ty.

This template’s compromise rule implies that you cannot use has_nothrow_-constructor in portable code. Although that’s technically true, the intention was to allow has_nothrow_constructor<Ty> to be derived from false_type even if the default constructor for the type Ty does not throw exceptions but not to allow it to be derived from true_type if the constructor for Ty does throw exceptions. Under this rule, has_nothrow_constructor<Ty>::value can be used to trigger optimizations that depend on a type’s constructor not throwing exceptions. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_nothrow_copy;

The type Ty must be a complete type.

The template specialization has_nothrow_copy<Ty> is derived from the class true_type if the copy constructor for the type Ty has a nothrow specifier or if the compiler can otherwise determine that it will not throw an exception. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_type or true_type without regard to the properties of the type argument Ty.

This template’s compromise rule implies that you cannot use has_nothrow_-copy in portable code. Although that’s technically true, the intention was to allow has_nothrow_copy<Ty> to be derived from false_type even if the copy constructor for the type Ty does not throw exceptions but not to allow it to be derived from true_type if the constructor for Ty does throw exceptions. Under this rule, has_nothrow_copy<Ty>::value can be used to trigger optimizations that depend on a type’s copy constructor not throwing exceptions. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_nothrow_assign;

The type Ty must be a complete type.

The template specialization has_nothrow_assign<Ty> is derived from true_type if the assignment operator for the type Ty has a nothrow specifier or if the compiler can otherwise determine that it will not throw an exception. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_type or true_type without regard to the properties of the type argument Ty.

This template’s compromise rule implies that you cannot use has_nothrow_-assign in portable code. Although that’s technically true, the intention was to allow has_nothrow_assign<Ty> to be derived from false_type even if the assignment operator for the type Ty does not throw exceptions but not to allow it to be derived from true_type if the assignment operator for Ty does throw exceptions. Under this rule, has_nothrow_assign<Ty>::value can be used to trigger optimizations that depend on a type’s assignment operator not throwing exceptions. When the result is wrong, the code that uses it will still be correct but won’t get the optimization. It’s probably safe to assume that implementations will follow this more restrictive rule.

template < class Ty > struct has_virtual_destructor;

The type Ty must be a complete type.

The template specialization has_virtual_destructor<Ty > is derived from true_type if the destructor for the type Ty is virtual. Otherwise, it is derived from false_type.

An implementation may define this template by deriving from false_type for all type arguments Ty.

This template’s compromise rule allows implementations to report that a type does not have a virtual destructor when it, in fact, does. This implies that you should use has_virtual_destructor<Ty>::value only to trigger optimizations that depend on a type having a virtual destructor. When the result is wrong, the code that uses it will still be correct but won’t get the optimization.

template < class Ty > struct is_signed;

The template specialization is_signed<Ty> is derived from true_type if the type Ty is a signed integral type. Otherwise, it is derived from false_-type.

template < class Ty > struct is_unsigned;

The template specialization is_unsigned<Ty> is derived from true_type if the type Ty is an unsigned integral type. Otherwise, it is derived from false_type.

template < class Ty > struct rank;

The template specialization rank<Ty> is derived from integral_constant<std::size_t, Rank>, where Rank is the number of dimensions of the array type Ty or 0 if Ty is not an array type.

For example, the rank of the type int is 0, the ranks of the types int[] and int[2] are both 1, and the ranks of the types int[][3] and int[2][3] are both 2.

template < class Ty, unsigned Idx = 0 > struct extent;

The template specialization extent<Ty, Idx> is derived from integral_-constant<std::size_t, Dim>, where Dim is the dimension of the Idx th bound of the type Ty.

If the type Ty is not an array type, if its rank is less than Idx, or if Idx == 0 and the type of Ty is array of unknown bound of Ty1, Dim is zero.

For example:

image

11.6. Type Relationships

Each of these class templates defines a type predicate. The templates determine whether their two type arguments have the required relationship.

template < class Ty1, class Ty2 > struct is_same;

The template specialization is_same<Ty1, Ty2> is derived from true_-type if the types Ty1 and Ty2 are the same type. Otherwise, it is derived from false_type.

template < class From, class To > struct is_convertible;

The type From must be a complete type or void.

The type To must be a complete, non-abstract type or void.

For all types From and To other than void, the template specialization is_convertible<From, To> is derived from true_type if there is an unambiguous, publicly accessible conversion from an lvalue of the type From to the type To. If there is an ambiguous conversion or a conversion that is not publicly accessible, the template specialization is ill formed. Otherwise, it is derived from false_type.

For all types Ty, the template specialization is_convertible<Ty, void> is derived from true_type.

For all types Ty other than void, the template specialization is_convert-ible<void, Ty> is derived from false_type.

template < class Base, class Derived > struct is_base_of;

The types Base and Derived must be complete types.

The template specialization is_base_of<Base, Derived> is derived from true_type if the type Base is the same type as Derived or if the type Base is a base class of Derived. Otherwise, it is derived from false_type.

11.7. Type Transformations

Each of these class templates defines a type transformation. The templates have a nested member named type that is the template type argument Ty appropriately transformed.

template < class Ty > struct remove_const;

The template specialization remove_const<Ty> holds a nested type named type that is the type Ty with any top-level const qualifier removed.

template < class Ty > struct remove_volatile;

The template specialization remove_volatile<Ty> holds a nested type named type that is the type Ty with any top-level volatile qualifier removed.

template < class Ty > struct remove_cv;

The template specialization remove_cv<Ty> holds a nested type named type that is the type Ty with any top-level const and volatile qualifiers removed.

template < class Ty > struct add_const;

The template specialization add_const<Ty> holds a nested type named type that is the type Ty if Ty is a reference or function or a type with a top-level const qualifier. Otherwise, it is const Ty.

template < class Ty > struct add_volatile;

The template specialization add_volatile<Ty> holds a nested type named type that is the type Ty if Ty is a reference or function or a type with a top-level volatile qualifier. Otherwise, it is volatile Ty.

template < class Ty > struct add_cv;

The template specialization add_cv<Ty> holds a nested type named type that is the same type as add_volatile< add_const<Ty> >::type.

template < class Ty > struct remove_reference;

The template specialization remove_reference<Ty> holds a nested type named type that is the type Ty1 if the type Ty is Ty1&. Otherwise, it is Ty.

template < class Ty > struct add_reference;

The template specialization add_reference< Ty > holds a nested type named type that is the type Ty& if Ty is not a reference type. Otherwise, it is Ty.

template < class Ty > struct remove_pointer;

The template specialization remove_pointer<Ty> holds a nested type named type that is the type Ty1 if the type Ty is Ty1*. Otherwise, it is Ty.

template < class Ty > struct add_pointer;

The template specialization add_pointer<Ty> holds a nested type named type that is the type Ty* if Ty is not a pointer type. Otherwise, it is Ty.

template < class Ty > struct remove_extent;

The template specialization remove_extent< Ty > holds a nested type named type that is the type Ty1 if the type Ty is Ty1[M]. Otherwise, it is Ty.

For example:

image

template < class Ty > struct remove_all_extents;

The template specialization remove_all_extents<Ty> holds a nested type named type that is the type Ty1 if the type Ty is Ty1[M]…[N]. Otherwise, it is Ty.

For example:

image

11.8. Alignment

Some hardware architectures impose alignment constraints, requiring that objects of certain types have addresses whose value is a multiple of some small number. In some cases, this is an absolute requirement: Attempting to access data that is not properly aligned causes a hardware fault. In others, it affects program performance: Attempting to access data that is not properly aligned requires additional bus cycles, slowing reading and writing of that data. Since alignment restrictions are imposed by hardware, the small number that defines the required alignment is usually a power of 2: typically, 4, 8, or 16. When we talk about the required alignment for some type, we usually say that it requires, for example, 8-byte alignment. This means that its address must be a multiple of 8.

template <class Ty> struct alignment_of;
template <std::size_t Len, std::size_t Align>
  struct aligned_storage;

The template specialization alignment_of<Ty> is derived from integral_constant<std::size_t, Align>, where Align is the alignment of objects of type Ty.

The argument Ty to the class template alignment_of<Ty> must name a complete type.

The template specialization aligned_storage< Len, Align > holds a nested type named type that names a POD type that can be used as uninitialized storage for an object whose alignment is a divisor of Align and whose size is less than or equal to Len. The value of the template argument Align must be the same as alignment_of<Ty>::value for some type Ty.[7]

The primary problem that these templates address is the need to create a properly aligned static storage block to use with placement new. This code doesn’t necessarily work correctly:

unsigned char data [ sizeof (double)];
double * loc = new (( void *)& data ) double ;
* loc = 1.0;

The problem here is that the array data can usually be aligned at a 1-byte boundary—that is, anywhere in memory—whereas the double* returned by placement new must point to memory that is properly aligned for an object of type double, which often requires a 4-byte or 8-byte boundary. The usual solution to this problem is to use a union of eight to ten fundamental types, in the hope that one of them will require the target platform’s worst-case alignment; a data array that can hold an object of that type will then be able to hold any data type. This approach has two drawbacks. First, alignment requirements for some types may be more severe than those of the types included in the union. In that case, those types will probably not be suitably aligned. Second, this approach wastes space for types whose alignment requirements are less severe than the worst case provided by the union. The solution using the TR1 alignment templates looks like this:

aligned_storage < sizeof ( double ),
  alignment_of <double>:: value>:: type data ;

double * loc = new ((void *)& data) double;
* loc = 1.0;

The definition of data in that code is a bit daunting, so let’s look at it piece by piece. First, sizeof(double) is the number of bytes needed to represent a value of type double. Second, alignment_of<double>::value is the required alignment for a value of type double. Those two values are passed as the template arguments Len and Align to the class template aligned_-storage. The resulting template instantiation has a nested type named type that has the required size and alignment. By defining data to be an object of that type, we guarantee that data has the required size and alignment. Since that type is a POD, it has a trivial destructor, so running a destructor on data that doesn’t make sense won’t cause any problems.

Further Reading

Modern C++ Design [Ale01] first brought template metaprogramming to the attention of everyday programmers.

C++ Template Metaprogramming [AG05] provides a gentler introduction to template metaprogramming.

C++ Templates [VJ03] gives the detailed rules for programming with templates.

Exercises

Exercise 1

For each of the following errors, write a simple test case containing the error, and try to compile it. In the error messages, look for the key words that relate to the error in the code.

1. Attempting to create an instance of integral_constant with a type that is not an integral type

2. Attempting to create an instance of integral_constant with a value that is not convertible to the integral type

3. Attempting to create an instance of is_pod with a type argument that is an incomplete type

4. Attempting to create an instance of is_empty with a type argument that is an incomplete type

5. Attempting to create an instance of is_polymorphic with a type argument that is an incomplete type

6. Attempting to create an instance of is_abstract with a type argument that is an incomplete type

7. Attempting to create an instance of has_trivial_constructor with a type argument that is an incomplete type

8. Attempting to create an instance of is_convertible with type arguments that are incomplete types

9. Attempting to create an instance of alignment_of with a type argument that is an incomplete type

10. Attempting to create an instance of aligned_storage with an Align argument of 17

Exercise 2

Write a program that contains the following typedefs for specializations of integral_constant:

1. int_0: holds an integral constant of type int whose value is 0

2. int_1: holds an integral constant of type int whose value is 1

3. uint_1: holds an integral constant of type unsigned int whose value is 1

To verify that they are distinct types, write a set of overloaded functions taking each of these types, create objects of these types, and call the corresponding overloaded function.

Exercise 3

Write a function template that takes an argument of a type that is a specialization of integral_constant and displays the value of the argument’s member value, the type named by the argument type’s nested typedef value_type, and the type named by the argument type’s nested typedef type. Call the function template with the typedefs true_type and false_type from the TR1 library and with the typedefs written in the preceding example.

Exercise 4

Write a function template that shows the result of applying each of the primary type predicates to the type of its template argument. Use this function template to verify the assertion in Section 11.3 that categories defined by the primary type predicates are complete and exclusive.

Exercise 5

Write a function template that checks whether each of the composite type predicates gives the correct result for the type of its template argument. Use this function template to check the results of the composite type predicates for several different types.

Exercise 6

Write a class template that takes one type argument and holds a typedef named type that names the same type as the type argument but with no const qualifier and with a volatile qualifier. Use the class template in a program that verifies that it works correctly by testing for each of those qualifiers.

Exercise 7

Write a function template to copy a range of values designated by a pair of iterators to a range designated by an output iterator using memcpy.[8] Write a function template to copy a range of values designated by a pair of iterators to a range designated by an output iterator, copying each element individually. Write a function template to copy a range of values designated by a pair of iterators to a range designated by an output iterator, using the first function if the type being copied has a trivial copy assignment operator and using the second function if it does not. Write a class that has a non-trivial copy assignment operator. Write a program that creates two arrays of objects of that type and two arrays of int values, copies the values from one of the arrays of objects to the other, and copies the values from one of the arrays of int values to the other, using the third copy function that you wrote. Verify that the copy operation does not use memcpy for the array of objects and that it uses memcpy for the array of int values.

Exercise 8

Write a function template that returns the transpose of a two-dimensional array with arbitrary extents.[9]

Exercise 9

Write a program that shows the alignment requirement given by the class template alignment_of for various integral, floating-point, and pointer types. If your compiler has a switch for controlling alignment—most do—compile the program with different alignment settings to see how the results are affected by the switch.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.40.36