A Proposal to add Type Traits to the Standard Library

Doc. no.: J16/03-0006 = WG21/N1424
Previous Doc. no.: J16/02-0003 = WG21/N1345
Date: 03 March 2003
Project: Programming Language C++
Wiki: http://www.research.att.com/~austern/cgi-bin/wiki?TypeTraits
Reply to: John Maddock <john_maddock@compuserve.com>

Contents

Motivation
Impact on the Standard
Design
    Separate classes, or a single monolithic class?
    Are Boolean properties integral-constant expressions or types?
    Templates or intrinsic operators?
    Selection of templates
    Compiler Support
Revision History
Unresolved Issues
Proposed Text
    Requirements
    Unary Type Traits
    Relationships Between Types
    Transformations Between Type
References
Acknowledgements

I. Motivation

This proposal forms part of the "infrastructure" of the standard library; it attempts to standardize templates that are often reinvented as bespoke solutions. These templates may be used by the end user, and are often used heavily by library implementers, including those who are writing third party libraries. There are three main use cases that help explain the motivation behind this proposal:

a) Supporting the widest possible range of types

Sometimes templates are not quite as "generic" as one would wish. The problem is that not all types are created equally, implying that some categories of types may need special handling. One example of this is a version of std::pair that can hold reference types[1]. The boost compressed_pair class[2] is a more advanced example of this; as well as being able to hold reference types, it will inherit from, rather than contain, empty classes in order to take advantage of the "zero sized base class optimisation".

b) For use with static (compile time) assertions

Often there is a contract that the template parameters to a class or function must obey. Usually if the template argument does not conform to the contract then the template will fail to compile. Often in such cases the position of the error may be deep inside several layers of template code, and it is often not at all easy for the user to deduce what the problem is, and whether it is their problem or the library's. One solution proposed for this is the idea of concept-checks[3], however a simpler solution is often possible using a static assertion[4]. For example the following code uses the boost type traits library to insist that the iterator arguments have random access semantics; if an error occurs then it will be generated early on in the procedure being instantiated, and can be commented with a description of the problem:

#include <iterator>
#include <boost/static_assert.hpp> 
#include <boost/type_traits.hpp> 

template <class RandomAccessIterator > 
RandomAccessIterator foo(RandomAccessIterator from, 
                         RandomAccessIterator to) 
{ 
   // this template can only be used with 
   // random access iterators, attempting to
   // use some other iterator type will result in
   // an error message here: 
   typedef typename std::iterator_traits
           <RandomAccessIterator>::iterator_category cat; 

   typedef int this_is_a_static_assertion[
         ::boost::is_convertible<
             cat*, 
             std::random_access_iterator_tag*
         >::value ? 1 : -1]; 
   // 
   // detail goes here... 
   //
   return from; 
}

Static assertions form part of a separate proposal[10].

c) Optimising code performance.

The classic example here is the algorithm std::copy; if the types being copied are POD's, then std::memcpy can be used to copy the data, rather than a slower "object by object" copy. The boost library has one example of this approach[1]. However this is by no means the only example: the SGI standard library[7] uses a type traits mechanism to achieve the same effect, while the Dinkumware standard library uses a function overloading approach to dispatch to the correct std::copy implementation. The fact that several vendors have reinvented this technique suggests that this is an area ripe for standardisation. It should be noted that while it is easiest to give examples of traits-based optimisations from the standard library, both third party libraries and user code can (and do) make use of these techniques. In the boost libraries for example, 17 of 57 top level headers make use of boost's type traits code either directly or indirectly.

Some real world experience comes from the Metrowerks standard library, Howard Hinnant reports: "The Metrowerks "STL" makes fairly heavy use of type traits for optimization purposes, especially the traits that test for trivial member functions (e.g. has_trivial_copy). We also use add_reference extensively in <algorithm> in order to support the issues outlined in [12]"

II. Impact on the Standard

This is basically a pure extension, it does not require any modification to existing standard library headers, and does not require any core language changes. However some of the templates listed in this proposal can not be fully implemented without some help from the compiler. The proposed text does not specify how this help is provided, and as a temporary measure sufficient lattitude is granted in the proposal to ensure that it is possible to provide an implementation that conforms to the semantics given, without compiler help (see the boost reference implementation[5]). Those templates that benefit from compiler help are noted below.

III. Design

There following key design decisions to be made in this proposal:

Separate classes, or a single monolithic class?

The boost type traits library (and this proposal) uses a separate traits class for each trait, where as, for example, both Loki[6] and the SGI type traits library[7] use a single type_traits class with multiple members. There are a number of advantages to the separate traits class approach:

Finally it should be noted that Loki's author has shifted away from a single type_traits class and towards separate classes in a type_traits namespace[9].

This approach contrasts with that of the current traits classes in the standard library (numeric_limits, and iterator_traits), these existing traits classes are closed for extention, where as type traits are more like an extensible concept, than a finite set of properties (as is the case for numeric_limits for example).

Are Boolean properties integral-constant expressions or types?

The boost type traits library originally used a static const bool to represent each true/false condition, where as the SGI type traits library[7] uses a type (__true_type or __false_type). The use of a type is more convenient for use with function overloading, whereas the use of an integral constant expression makes it much easier to combine traits using logical expressions - something that has been often found to be necessary during the development of the boost library. The following example shows how std::copy can be optimized by combining value based type traits:

//
// copy:
// same semantics as std::copy
// calls memcpy where appropriate.
//

namespace detail{

template <bool b>
struct copier
{
   template<typename I1, typename I2>
   static I2 do_copy(I1 first, I1 last, I2 out)
   {
      while(first != last)
      {
         *out = *first;
         ++out;
         ++first;
      }
      return out;
   }
};

template <>
struct copier<true>
{
   template<typename I1, typename I2>
   static I2* do_copy(I1* first, I1* last, I2* out)
   {
      memcpy(out, first, (last-first)*sizeof(I2));
      return out+(last-first);
   }
};


}

template<typename I1, typename I2>
inline I2 copy(I1 first, I1 last, I2 out)
{
   typedef typename boost::remove_cv<typename std::iterator_traits<I1>::value_type>::type v1_t;
   typedef typename boost::remove_cv<typename std::iterator_traits<I2>::value_type>::type v2_t;
   return detail::copier<
         ::boost::is_same<v1_t, v2_t>::value
         && ::boost::is_pointer<I1>::value
         && ::boost::is_pointer<I2>::value
         && ::boost::has_trivial_assign<v1_t>::value

       >::do_copy(first, last, out);
}

There is one potential problem with integral constant members that should be raised however: these require an out of line definition as well as an inline initialization (but see core issues 48 and 82).   If static const bool members are used, then one implementation strategy that avoids the problem of having to provide lots of definitions for these static members, would be to have the traits classes inherit from a common base class:

template <class T, T v> struct integral_constant{ static const T value = v; };
template <class T, T v> T integral_constant<T,v>::value;

typedef integral_constant<bool, true>  true_type;
typedef integral_constant<bool, false> false_type;

template <class T> struct is_void : public false_type {};
template <> struct is_void<void> : public true_type {};
// etc

Note that there is no requirement to implement the traits classes in this manner: an enum could be used here in place of a static const bool. This scheme does however make it trivial to select a function overload based on whether a traits class inherits from true_type or false_type.

An important use case for providing a type representation for the result of Boolean traits comes from the template metaprogramming, the discipline where class templates are treated as compile-time invocable entities (metafunctions), and where experience has shown that to build a consistent and coherent framework of well-interpolating components it's crucially important for the metafunctions to provide a single common form of invocation [11]. Since it is possible to wrap an integral constant in a type, but not vice-versa, the implementation of boost type traits library was re-written to provide the result of Boolean trait templates through both their static const bool value member and the nested type typedef exposing the corresponding specialization of an integral constant type wrapper.

This proposal follows the boost traits library approach; the template and typedefs:

template <class T, T v> integral_constant;
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

have been added to act as type-based aliases for integral constant expressions. Likewise the type_traits requirements and descriptions have been updated to make use of this helper template, for example:

template <class T> struct is_void{
  static const bool value = implementation defined;
  typedef bool value_type;
  typedef integral_constant<value_type, value> type;
};

This gives us the best of both worlds, allowing the programmer to use either a type or a value depending upon whichever is the most convenient. For example the std::copy example could be implemented as follows:

template<typename T>
T* do_copy(const T* first, const T* last, T* out, const true_type&)
{ /* optimised copy here */ }

template<typename I1, typename I2, bool b>
I2 do_copy(I1 first, I1 last, I2 out, const integral_const<bool,b>&)
{ /* non-optimised copy here */ }

template<typename I1, typename I2>
inline I2 copy(I1 first, I1 last, I2 out)
{
   typedef typename std::iterator_traits<I1>::value_type value_type;
   typedef typename has_trivial_assign<value_type>::type trivial_assign;
   return do_copy(first, last, out, trivial_assign());
}

This example can be simplified even further if we insist that for each trait X, then X is implicitly convertible to X::type: this can be accomplished in one of two ways: by inheritance from a common base class (as above), or by providing a member conversion operator:

template <class T> struct is_void{
  static const bool value = implementation defined;
  typedef bool value_type;
  typedef integral_constant<value_type, value> type;
  operator type()const;
};

This proposal does not specify which method should be used, but it does specify that the conversion is possible; even though this is merely a convenience it is nonetheless a useful one. The std::copy example can now be simplified further still:

template<typename T>
T* do_copy(const T* first, const T* last, T* out, const true_type&)
{ /* optimised copy here */ }

template<typename I1, typename I2, bool b>
I2 do_copy(I1 first, I1 last, I2 out, const integral_constant<bool,b>&)
{ /* non-optimised copy here */ }

template<typename I1, typename I2>
inline I2 copy(I1 first, I1 last, I2 out)
{
   typedef typename std::iterator_traits<I1>::value_type value_type;
   return do_copy(first, last, out, has_trivial_assign<value_type>());
}

Templates or intrinsic operators?

There has been some fairly intense discussion on boost mailing list about the is_convertible template, one suggestion was that since only expressions (and not types) are convertible to a type or not, that is_convertible should be an intrinsic binary operator whose first argument was an expression, and whose second was a type. There are two key advantages to this approach: it more closely mirrors the text of the standard, and therefore standard wording is easier to formulate, and the result can be context sensitive:

class B;
class A
{
    A(const B&);
    /* details omitted */
};

Here class B is convertible to A only within the scope of A; if is_convertible is a template then the one definition rule requires that is_convertible<B,A>::value have one value only, and must not be sensitive to context.

On the other hand, inspite of its failings, the is_convertible template has proven to be extremely useful in practice, and only very rarely does a corner case arise where it fails to do the right thing. The template also appears to be easily understood by end users, even if getting the standardese correct is tricky. To put this in perspective there are about 50 uses of the is_convertible template within the boost library, and none of these would obviously benefit from an operator style of syntax, some examples include:

Likewise is_convertible has been used (outside of boost) to ensure that code "does the right thing", for example the constructor:

template <class T, class Allocator>
template <class I>
vector<T,Allocator>::vector<I>(I first, I last, const Allocator& a);

Needs to behave differently depending upon whether type I is an iterator or a numeric type. One solution is to default initialise the object, and then perform a compiler time dispatch to different a member function depending upon whether the expression:

is_convertible<I,size_type>::value && is_convertible<I,value_type>::value

is true or not.

The author's belief is that as far as the DR is concerned, the is_convertible template class is the right way to go; it is better understood, and has shown itself to be useful as compared to an as yet untried intrinsic operator. It also fits well into the current type traits conceptual framework. However, implementers should be encouraged to experiment with an intrinsic convertibility operator, and this subject may need to be revisited prior to the next standard.

Instantiation of template arguments

There have been requests from the authors of boostís MPL that the proposal document whether instantiation any of the unary traits class templates in this proposal will result in the instantiation of a template argument that is itself a template-id (there are some metaprogramming idioms [11] where it is important that instantiation does not occur). Section (14.7.1) implies that if it is a precondition that the a template argument be a complete type (because the semantics of the class depend upon it), then instantiation of that argument (if it is a template-id) is essential in order to determine whether the type has a specific property or not. Similary, for those class templates that do not mandate that the template argument(s) are complete types, there are nonetheless implementations that may require that the type is a complete type (for example is_enum and any other templates that rely upon it). Therefore, mandating that these templates work with incomplete types or with template-idís which can not be instantiated, results in compiler support being required where it was not required before. Nonetheless MPL's authors believe that this is a sufficiently important problem that some wording requiring traits class templates not to instantiate their template arguments has been added, albeit with latitude permitting instantiation to occur as a temporary workaround until such time as compiler support becomes available.

Selection of templates

Perhaps the most difficult part of this proposal is deciding which traits classes should be included. The boost type traits library has undergone several revisions, with some templates being removed and others added. This proposal includes most of the current boost type traits classes along with a small number of others that are motivated by Loki[6]. 

Several templates that were in the original proposal were removed after feedback from the LWG (add_const, add_volatile, add_cv, add_pointer, add_reference). These were then reinstated after strong complaints from some of boost's users. The rationale is that these templates are all used as compile time functors which transform one type to another, for example Aleksey Gurtovoy provides the following example, which uses the boost metaprogramming library[11] to transform one type list to another, using type traits as compile time functors:

// transforms 'tuple<T1,T2,..,Tn>'
// to 'tuple<T1 const&,T2 const&,..,Tn const&>'
template< typename Tuple >
struct tuple_of_refs
{
   // transform tuple element types
   typedef typename mpl::transform<
      typename Tuple::elements,
      add_reference< add_const<_1> > // here!
   >::type refs;
   typedef typename tuple_from_sequence<refs>::type type;
};

template< typename Tuple >
typename tuple_of_refs<Tuple>::type
tuple_ref(Tuple const& t)
{
    return typename tuple_of_refs<Tuple>::type(t);
}

Although such uses have been rare up until now, they are becoming much more common, especially with the introduction of metaprogramming frameworks such as boost's MPL. In this context these templates are analogous to those in <functional>, in both cases the implementations are trivial, but unless these are provided in the standard library, users will have to constantly reinvent the same solution. Provision of these templates also allows for compiler warning suppression in a portable manner:

template <class T>
class foo
{
    // The following declaration does not produce a
    // warning if T is already const, or is a reference type:
    typedef typename std::add_const<T>::type type; 
    /* details omitted */
};

Feedback from the LWG suggested renaming "is_same" to "is_identical", however there was strong feeling among the boost community for retaining is_same, therefore no change has been made.

There have been three late additions to this proposal:

template <class T> struct is_signed;
template <class T> struct is_unsigned;
template <class T> struct is_abstract;

These additions may need reviewing as the proposal progresses - although no great problems are anticipated from them - in particular is_signed and is_unsigned have been part of the Metrowerks type traits library for some time. There have been some fairly persistent requests for the addition of these three templates to boost, and they are likely to added soon.

There have been one or two requests that haven't made it into the proposed text, mainly because these are templates that either don't sit well in the current design structure, or because their usefulness isn't clear. These are documented here in case the committee wants to discuss and/or add these to the text.

From Dave Abrahams:
"Here's another one we need:
has_common_base_class<T,U>::value
This is the one which allows us to explicitly construct empty classes on top of one another. I suppose you could argue that the compiler should just do this automatically with the empty base optimization, but the ABIs of many compilers are now fixed and they will never (for practical purposes) be able to do it."
From Andrei Alexandrescu:
"An important trait that's missing is the transformation same_align_pod, which would yield a POD type with the same alignment requirements as the passed-in type. This type would allow one to build a union that obeys specific alignment requirements."

Compiler Support

The following templates require compiler support for a fully conforming implementation:

template <class T> struct is_class;
template <class T> struct is_union;
template <class T> struct is_enum;
template <class T> struct is_polymorphic;
template <class T> struct is_empty;
template <class T> struct has_trivial_constructor;
template <class T> struct has_trivial_copy;
template <class T> struct has_trivial_assign;
template <class T> struct has_trivial_destructor;
template <class T> struct has_nothrow_constructor;
template <class T> struct has_nothrow_copy;
template <class T> struct has_nothrow_assign;
template <class T> struct is_pod;
template <class T> struct is_abstract;

In all of these cases however, a nearly-conforming implementation can be provided without any compiler support (see the boost reference implementation [5]), in addition temporary latitude is provided in the proposed TR text to permit a library-only implementation (see implementation requirements).

This text does not specify how the compiler support is provided, and nor will it do so, as the author believes that this is over-specification. There are multiple methods for providing this support, the relative merits of which probably depend most upon the compiler's internal structure. To put this in perspective, library vendors will need to do only a small amount of library configuration if they need to support multiple compilers, where as specifying the method used would entail duplicating a large portion of the existing descriptions in this text, increasing the maintenance cost, and the likelihood of defects.

To date three vendors provide some degree of compiler type traits support:

IV Revision History

Added comparison to numeric_limits and iterator traits design.

Added discussion on is_convertible.

Rewritten the compiler support section: added comments on existing implementations.

Allow implementation of integral members as integral constant expressions, not necessarily as static const bool's.

Added section on implementation variance to permit a conforming implementation within the current language.

Clarified language on use of T and cv-qualified T.

Removed permission for user defined specialisations of type categories.

is_float renamed is_floating_point.

is_member_pointer demoted to a composite type category; added is_member_object_pointer and is_member_function_pointer to the primary type category traits.

Removed all the author's explanitory notes.

Simplified definition of is_compound.

Renamed has_nothrow_construct has_nothrow_constructor for consistency with has_trivial_constructor.

Rewritten definition of non-throwing text, may still not be quite right.

Renamed is_base_and_derived to is_base_of: this change has caused a fair amout of controversy, with many boosters prefering the more verbose original name.

Renamed is_POD to is_pod for consistency with other all-lower-case names.

Added additional members type, value_type and operator type, to Unary and binary type traits.

Added the new templates is_unsigned, is_signed and is_abstract.

Added section(s) on template class instantiation.

V Unresolved issues

It is unclear what the name of is_base_of should be: this is called is_base_and_derived in the boost library - the intent is that the name is so explicit that the user can't get the template parameters the wrong way around - likewise it is easier on users reading someone else's code. However there is no getting around the fact that this is an extremely verbose name, so perhaps is_base_of is a better choice.

Is is unclear how remove_pointer should behave with pointers to member (functions). Since there are no such types as "member" or "member function returning type" (you can't dereference member pointers for example), the current version is logical in leaving member pointers unchanged. However one boost user has requested that remove_pointer<T (U::*)>::type evaluates to T, on the grounds that this transformation is sometimes useful.

It is unclear whether the newly added template is_abstract requires compiler support or not, to be on the safe side it is listed as requiring support, but this may be removed at a later date.


Proposed Text


Type Library

This clause describes components used by C++ programs, particularly in templates, to: support the widest possible range of types, optimise template code usage, and detect type related user errors.

The type library provides three facilities: access to the properties of a particular type, access to the relationships (if any) between two types, and transformations from one type to another; as summarised in table TT1

Table TT1 - Type Library Summary

Clause

Header

Requirements

 

Unary type traits

<type_traits>

Relationships between types

<type_compare>

Transformations between types

<type_transform>

 

TT.1 Requirements

TT.1.1 Unary Type Traits

In table TT2, X is a class template that is a unary type trait and T is any arbitrary type.

Table TT2 - UnaryTypeTrait requirements

Expression

Return type

Requirement

X<T>::value

bool

An integral constant expression that is true if T has the specified trait, and false otherwise.

X<T>::value_type

bool

A type that is the type of X<T>::value, this is always bool for UnaryTypeTraits.

X<T>::type

integral_constant<bool,value>

A type for use in situations where a typename is more appropriate than a value. The class template integral_constant is declared in <type_traits>.

X<T>::type t = X<T>()

 

Both lvalues of type X<T> const& and rvalues of type X<T> are implicitly convertible to X<T>::type.

TT.1.2 Binary Type Traits

In table TT3, X is a class template that is a binary type trait and T and U are any arbitrary types.

Table TT3 - BinaryTypeTrait requirements

Expression

Return type

Requirement

X<T,U>::value

bool

An integral constant expression that is true if T is related to U by the relation specified, and false otherwise.

X<T,U>::value_type

bool

A type that is the type of X<T,U>::value, this is always bool for BinaryTypeTraits.

X<T,U>::type

integral_constant<bool,value>

A type for use in situations where a typename is more appropriate than a value. The class template integral_constant is declared in <type_traits>.

X<T,U>::type t = X<T,U>()

 

Both lvalues of type X<T,U> const& and rvalues of type X<T,U> are implicitly convertible to X<T,U>::type.

TT.1.3 Transformation Traits

In table TT4, X is a class template that is a transformation trait and T is any arbitrary type.

Table TT4 - TransformationTrait requirements

Expression

Requirement

X<T>::type

The result is a type that is the result of applying transformation X to type T.

 

TT.2 Unary Type Traits

This sub-clause contains templates that may be used to query the properties of a type at compile time.

All of the class templates defined in this header satisfy the UnaryTypeTrait requirements.

For all of the class templates declared in this clause, all members declared static const shall be defined in such a way that they are usable as integral constant expressions.

For all of the class templates X declared in this clause, both rvalues of type X const and lvalues of type X const& shall be implicitly convertible to X::type. Whether this is accomplished by inheritance or by a member operator is unspecified. For exposition only the class templates in this clause are shown with a member conversion operator:

operator type() const;

which shall return type() in all cases.

For all of the class templates X declared in this clause, instantiating that template with a template-argument that is a class template specialization, may result in the implicit instantiation of the template argument if and only if the semantics of X require that the argument must be a complete type.

TT.2.1 Header <type_traits> synopsis

namespace std{

// helper class:
template <class T, T v> struct integral_constant;
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

// Primary type categories:
template <class T> struct is_void;
template <class T> struct is_integral;
template <class T> struct is_floating_point;
template <class T> struct is_array;
template <class T> struct is_pointer;
template <class T> struct is_reference;
template <class T> struct is_member_object_pointer;
template <class T> struct is_member_function_pointer; 
template <class T> struct is_enum;
template <class T> struct is_union;
template <class T> struct is_class;
template <class T> struct is_function;

// composite type categories: 
template <class T> struct is_arithmetic;
template <class T> struct is_fundamental;
template <class T> struct is_object;
template <class T> struct is_scalar;
template <class T> struct is_compound;
template <class T> struct is_member_pointer;

// type properties:
template <class T> struct is_const;
template <class T> struct is_volatile;
template <class T> struct is_pod;
template <class T> struct is_empty;
template <class T> struct is_polymorphic;
template <class T> struct is_abstract;
template <class T> struct has_trivial_constructor;
template <class T> struct has_trivial_copy;
template <class T> struct has_trivial_assign;
template <class T> struct has_trivial_destructor;
template <class T> struct has_nothrow_constructor;
template <class T> struct has_nothrow_copy;
template <class T> struct has_nothrow_assign;
template <class T> struct is_signed;
template <class T> struct is_unsigned;

} // namespace std


TT.2.2 Helper classes

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

The class template integral_constant and its associated typedefs true_type and false_type are for use in situations where a type rather than a value is required.

TT.2.3 Primary Type Categories

The primary type categories correspond to the descriptions given in section 3.9.

For any given type T, exactly one of the primary type categories shall have its member value evaluate to true.

For any given type T, the result of applying one of these templates to T, and to cv-qualified T shall yield the same result.

Undefined behaviour results if any C++ program adds specializations for any of the class templates defined in this clause.

template <class T> struct is_void {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is void or a cv-qualified void. Otherwise defined to be false.

template <class T> struct is_integral {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is an integral type (3.9.1). Otherwise defined to be false.

template <class T> struct is_floating_point {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a floating point type (3.9.1). Otherwise defined to be false.

template <class T> struct is_array {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is an array type (3.9.2). Otherwise defined to be false.

template <class T> struct is_pointer {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a pointer type (3.9.2), this includes all function pointer types, but not pointers to members or member functions. Otherwise defined to be false.

template <class T> struct is_reference {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a reference type (3.9.2), this includes all reference to function types. Otherwise defined to be false.

template <class T> struct is_member_object_pointer {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a pointer to a data member. Otherwise defined to be false.

template <class T> struct is_member_function_pointer {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a pointer to a member function. Otherwise defined to be false.

template <class T> struct is_enum {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is an enumeration type (3.9.2). Otherwise defined to be false.

template <class T> struct is_union {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
}; 

value: defined to be true if T is a union type (3.9.2). Otherwise defined to be false.

template <class T> struct is_class {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
}; 

value: defined to be true if T is a class type (3.9.2), in this context unions are not considered to be class types. Otherwise defined to be false.

template <class T> struct is_function {
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a function type (3.9.2). Otherwise defined to be false.

TT.2.4 Composite type traits

These templates provide convenient compositions of the primary type categories, corresponding to the descriptions given in section 3.9.

For any given type T, the result of applying one of these templates to T, and to cv-qualified T shall yield the same result.

Undefined behaviour results if any C++ program adds specializations for any of the class templates defined in this clause.

template <class T> struct is_arithmetic { 
   static const bool value = is_integral<T>::value || is_floating_point<T>::value; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is an arithmetic type (3.9.1). Otherwise defined to be false.

template <class T> struct is_fundamental{
   static const bool value = 
      is_integral<T>::value 
      || is_floating_point<T>::value 
      || is_void<T>::value; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a fundamental type (3.9.1). Otherwise defined to be false.

template <class T> struct is_object{
   static const bool value = 
      !(is_function<T>::value 
         || is_reference<T>::value 
         || is_void<T>::value); 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is an object type (3.9). Otherwise defined to be false.

template <class T> struct is_scalar{
   static const bool value = 
      is_arithmetic<T>::value 
      || is_enum<T>::value
      || is_pointer<T>::value 
      || is_member_pointer<T>::value; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a scalar type (3.9). Otherwise defined to be false.

template <class T> struct is_compound{
   static const bool value = !is_fundamental<T>::value; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a compound type (3.9.2). Otherwise defined to be false.

template <class T> struct is_member_pointer {
   static const bool value = 
       is_member_object_pointer<T>::value
       || is_member_function_pointer<T>::value; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a pointer to a member or member function. Otherwise defined to be false.

TT.2.5 Type properties

These templates provide access to some of the more important properties of types; they reveal information which is available to the compiler, but which would not otherwise be detectable in C++ code.

Except where specified, it is undefined whether any of these templates have any full or partial specialisations defined. It is permitted for the user to specialise any of these templates on a user-defined type, provided the semantics of the specialisation match those given for the template in its description.

template <class T> struct is_const{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is const-qualified (3.9.3). Otherwise defined to be false.

template <class T> struct is_volatile{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is volatile-qualified (3.9.3). Otherwise defined to be false.

template <class T> struct is_pod{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if T is a POD type (3.9). Otherwise defined to be false.

template <class T> struct is_empty{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if T is an empty class (10). Otherwise defined to be false.

template <class T> struct is_polymorphic{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if T is a polymorphic class (10.3). Otherwise defined to be false.

template <class T> struct is_abstract{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if T is a abstract class (10.4). Otherwise defined to be false.

template <class T> struct has_trivial_constructor{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the default constructor for T is trivial(12.1). Otherwise defined to be false.

template <class T> struct has_trivial_copy{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the copy constructor for T is trivial (12.8). Otherwise defined to be false.

template <class T> struct has_trivial_assign{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the assignment operator for T is trivial (12.8). Otherwise defined to be false.

template <class T> struct has_trivial_destructor{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the destructor for T is trivial (12.4). Otherwise defined to be false.

template <class T> struct has_nothrow_constructor{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the default constructor for T has an empty exception specification, or can otherwise be deduced never to throw an exception. Otherwise defined to be false.

template <class T> struct has_nothrow_copy{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the copy constructor for T has an empty exception specification, or can otherwise be deduced never to throw an exception. Otherwise defined to be false.

template <class T> struct has_nothrow_assign{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template argument T shall be a complete type.

value: defined to be true if the assignment operator for T has an empty exception specification, or can otherwise be deduced never to throw an exception. Otherwise defined to be false.

template <class T> struct is_signed{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is a signed integral type (3.9.1). Otherwise defined to be false.

template <class T> struct is_unsigned{
   static  const bool value                    = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T is an unsigned integral type (3.9.1). Otherwise defined to be false.

TT.3 Relationships between Types

This sub-clause contains templates that may be used to query the relationships between two types at compile time.

All of the templates in this header satisfy the BinaryTypeTrait requirements.

For all of the class templates declared in this clause, all members declared static const shall be defined in such a way that they are usable as integral constant expressions.

For all of the class templates X declared in this clause, both rvalues of type X const and lvalues of type X const& shall be implicitly convertible to X::type. Whether this is accomplished by inheritance or by a member operator is unspecified. For exposition only the class templates in this clause are shown with a member conversion operator:

operator type() const;

which shall return type() in all cases.

TT.3.1 Header <type_compare> synopsis

namespace std{

// helper classes:
template <class T, T v> struct integral_constant;
typedef integral_constant<bool, true>  true_type;
typedef integral_constant<bool, false> false_type;

// type relations:
template <class T, class U> struct is_same;
template <class From, class To> struct is_convertible;
template <class Base, class Derived> struct is_base_of;

} // namespace std

Inclusion of the header <type_compare> shall make the type integral_constant, and itís associated typedefs true_type and false_type defined in <type_traits> visible. Whether any other types declared in <type_traits> are made visible by the inclusion of <type_compare> is implementation defined.

TT.3.2 Type Relationships

template <class T, class U> struct is_same{
   static const bool value = false; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};
template <class T> struct is_same<T,T>{
   static const bool value = true; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true if T and U are the same type. Otherwise defined to be false.

template <class From, class To> struct is_convertible {
   static const bool value = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

value: defined to be true only if an imaginary lvalue of type From is implicitly-convertible to type To (4.0). Otherwise defined to be false. Special conversions involving string-literals and null-pointer constants are not considered (4.2, 4.10 and 4.11). No function-parameter adjustments (8.3.5) are made to type To when determining whether From is convertible to To: this implies that if type To is a function type or an array type, then value must necessarily evaluate to false.

The expression is_convertible<From,To>::value is ill-formed if:

Type From, is a void or incomplete type (3.9).

Type To, is an incomplete, void or abstract type (3.9).

The conversion is ambiguous, for example if type From has multiple base classes of type To (10.2).

Type To is of class type and the conversion would invoke a non-public constructor of To (11.0 and 12.3.1).

Type From is of class type and the conversion would invoke a non-public conversion operator of From (11.0 and 12.3.2).

template < class Base, class Derived> struct is_base_of {
   static const bool value = implementation_defined; 
   typedef bool                                value_type;
   typedef integral_constant<value_type,value> type;
   operator type()const;
};

Preconditions: template arguments Base and Derived shall both be complete types.

value: defined to be true only if type Base is a base class of type Derived (10). Otherwise defined to be false.

TT.4 Transformations between Types

This sub-clause contains templates that may be used to transform one type to another following some predefined rule.

All of the templates in this header satisfy the TransformationTrait requirements.

TT.4.1 Header <type_transform> synopsis

namespace std{

// const-volatile modifications:
template <class T> struct remove_const;
template <class T> struct remove_volatile;
template <class T> struct remove_cv;
template <class T> struct add_const;
template <class T> struct add_volatile;
template <class T> struct add_cv;

// reference modifications:
template <class T> struct remove_reference;
template <class T> struct add_reference;

// array modifications:
template <class T> struct remove_bounds;

// pointer modifications:
template <class T> struct remove_pointer;
template <class T> struct add_pointer;

} // namespace std

Const-volatile Modifications

template <class T> struct remove_const{
   typedef T type; 
};
template <class T> struct remove_const<T const>{
   typedef T type; 
};

type: defined to be a type that is the same as T, except that any top level const-qualifier has been removed. For example: remove_const<const volatile int>::type evaluates to volatile int, where as remove_const<const int*> is const int*.

template <class T> struct remove_volatile{
   typedef T type; 
};
template <class T> struct remove_volatile<T volatile>{
   typedef T type; 
};

type: defined to be a type that is the same as T, except that any top level volatile-qualifier has been removed. For example: remove_const<const volatile int>::type evaluates to const int, where as remove_const<volatile int*> is volatile int*.

template <class T> struct remove_cv{
   typedef typename remove_const<typename remove_volatile<T>::type>::type type; 
};

type: defined to be a type that is the same as T, except that any top level cv-qualifiers have been removed. For example: remove_cv<const volatile int>::type evaluates to int, where as remove_cv<const volatile int*> is const volatile int*.

template <class T> struct add_const{
   typedef T const type; 
};

type: if T is a reference, function, or top level const-qualified type, then the same type as T, otherwise T const.

template <class T> struct add_volatile{
   typedef T volatile type; 
};

type: if T is a reference, function, or top level const-qualified type, then the same type as T, otherwise T volatile.

template <class T> struct add_cv{
   typedef typename add_const< typename add_volatile<T>::type >::type type; 
};

type: the same type as add_const< add_volatile<T>::type >::type.

Reference Modifications

template <class T> struct remove_reference{
   typedef T type;
};
template <class T> struct remove_reference<T&>{
   typedef T type;
};

type: defined to be a type that is the same as T, except any reference qualifier has been removed.

template <class T> struct add_reference{
   typedef T& type;
};
template <class T> struct add_reference<T&>{
   typedef T& type;
};

type: if T is a reference type, then T, otherwise T&.

Array Modifications

template <class T> struct remove_bounds{
   typedef T type;
};
template <class T, std::size_t N> struct remove_bounds<T[N]>{
   typedef T type;
};

type: defined to be a type that is the same as T, except any top level array bounds have been removed.

Pointer Modifications

template <class T> struct remove_pointer{
   typedef T type;
};
template <class T> struct remove_pointer<T*>{
   typedef T type;
};
template <class T> struct remove_pointer<T* const>{
   typedef T type;
};
template <class T> struct remove_pointer<T* volatile>{
   typedef T type;
};
template <class T> struct remove_pointer<T* const volatile>{
   typedef T type;
};

type: defined to be a type that is the same as T, except any top level indirection has been removed. Note: pointers to members are left unchanged by remove_pointer.

template <class T> struct add_pointer{
   typedef typename remove_bounds<typename remove_reference<T>::type>::type* type;
};

type: defined to be a type that is the result of the expression &t, where t is an imaginary lvalue of type T.

TT.5 Implementation requirements

The behaviour of all the class templates defined in <type_traits>, <type_compare> and <type_transform> shall conform to the specifications given, except where noted below.

The latitude granted to implementers in this clause is temporary, and will be removed in future revisions of this document.

If there is no means by which the implementation can differentiate between class and union types, then the class templates is_class and is_union need not be provided.

If there is no means by which the implementation can detect polymorphic types, then the class template is_polymorphic need not be provided.

If there is no means by which the implementation can detect abstract types, then the class template is_abstract need not be provided.

It is unspecified under what circumstances, if any, is_empty<T>::value evaluates to true.

It is unspecified under what circumstances, if any, is_pod<T>::value evaluates to true, except that, for all types T:

is_pod<T>::value == is_pod<remove_bounds<T>::type>::value
is_pod<T>::value == is_pod<T const volatile>::value
is_pod<T>::value >= (is_scalar<T>::value || is_void<T>::value)

It is unspecified under what circumstances, if any, has_trivial_*<T>::value evaluates to true, except that:

has_trivial_*<T>::value == has_trivial_*<remove_bounds<T>::type>::value
has_trivial_*<T>::value >= is_pod<T>::value

It is unspecified under what circumstances, if any, has_nothrow_*<T>::value evaluates to true.

There are trait templates whose semantics do not require their argument(s) to be completely defined, nor does such completeness in any way affect the exact definition of the traits class template specializations. However, in the absence of compiler support these traits cannot be implemented without causing implicit instantiation of their arguments; in particular: is_class, is_enum, and is_scalar. For these templates, it is unspecified whether their template argument(s) are implicitly instantiated when the traits class is itself instantiated.


References

  1. John Maddock and Steve Cleary, "C++ Type Traits", Dr Dobbs Journal, October 2000.
  2. Steve Cleary, Beman Dawes, Howard Hinnant and John Maddock, "compressed_pair", Boost library documentation.
  3. Jeremy Siek, "Concept Checking", C++ Template Workshop 2000.
  4. John Maddock, "static assert", Boost library documentation.
  5. Steve Cleary, Beman Dawes, Aleksey Gurtovoy, Howard Hinnant, Jesse Jones, Mat Marcus, John Maddock and Jeremy Siek, "Type traits", Boost library documentation.
  6. Andrei Alexandrescu, "Modern C++ Design: Generic Programming and Design Patterns Applied", Addison-Wesley, 2001.
  7. SGI, "The SGI standard template library".
  8. Emily Winch, Heterogenous lists of named objects, Second C++ Template Programming Workshop 2001.
  9. Andrei Alexandrescu, "Generic<Programming>: Typed Buffers (II)", Oct CUJ C++ Experts, 2001.
  10. Robert Klarer and John Maddock, Proposal to Add Static Assertions to the Core Language, 2002.
  11. Aleksey Gurtovoy and David Abrahams, The Boost C++ Metaprogramming Library, 2002.
  12. Klaus Kreft and Angelika Langer, Effective Standard C++ Library: Explicit Function Template Argument Specification and STL A New Language Feature and Its Impact on Old Programming Techniques, C/C++ Users Journal, December 2000.

Acknowledgements

The author is particularly indebted to everyone who has worked on the boost type traits library[5], but also to David Abrahams, Darin Adler, Beman Dawes, Daniel Frey, Aleksey Gurtovoy, Howard Hinnant, Richard Peters, Gabriel Dos Reis and Emily Winch for their helpful comments on various drafts of this document, and to the Library Working Group Reviewers (Martin Sebor, Dietmar Kuehl and Benjamin Kosnik).