A Proposal to add Type Traits to the Standard Library

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

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 do not form part of this proposal, however they could easily form part of another proposal.

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.

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 it is possible to provide an implementation that almost 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 are two 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].

Are Boolean properties integral-constant expressions or types?

The boost type traits library (and this proposal) uses 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, however 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. In addition it is possible to convert a value to a type more easily than a type can be converted to a value. In most cases though conversion to a type should not be necessary - for example the std::copy optimisation example does not require this:

//
// 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: these require an out of line definition as well as an inline initialisation (but see issues 48 and 82). 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 <bool b> struct boolian_traits{ static const bool value = b; };
template <bool b> bool boolian_traits<b>::value;

typedef boolian_traits<true> true_type;
typedef boolian_traits<false> false_type;

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

This does not form part of this proposal, and this particular issue is ignored in the boost library, however there may be sufficient merit to this scheme to make it worth while adding to the proposed text. This scheme also makes it trivial to select a function overload based on whether a traits class inherits from true_type or false_type.

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 all of the current boost type traits classes along with a small number of others that are motivated by Loki[6]. The assumption is that the committee will find it easier to strike out templates from the proposal, than to add new templates (although the latter isn't necessarily ruled out). Some specific comments that have been made include:

From Andrei Alexandrescu:

"I understand the symmetry argument for adding add_const, add_volatile, add_cv, and add_pointer, however I would argue in favor of eliminating them. The language-provided equivalents are just simpler and nicer."

The names chosen for these templates is inevitably somewhat arbitrary, it may be that some could be better named:

From Howard Hinnant:

"template <class T> struct is_float;

I think is_floating is a better name. Since we have a built-in type named float, I'm not sure if is_float refers to all floating types or just to float. It makes me wonder if there is also an is_double. As this library closely mirrors 3.9 [basic.types], and 3.9.1/8 refers to these types as "floating point", then is_floating_point might be another good name."

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 into the current design structure, or because they're 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 in order to be implemented:

template <class T> struct is_class;
template <class T> struct is_union;
template <class T> struct is_polymorphic;

The following template has an implementation that is likely to be dependent upon the above:

template <class T> struct is_empty;

The following templates do not require compiler support in order to conform to the letter of this proposal, however they greatly benefit from having such support:

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_construct;
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_empty;

The mechanism of compiler support is not given in the proposal text, however several people have asked that this be specified, and there are a number of possibilities for this:

  1. The mechanism is left unspecified - it is up to each individual implemention to decide how best to support these templates - this option has the disadvantage that portable standard library implementations become much more difficult than they would otherwise be.
  2. For each template that requires compiler support, define an associated operator (__is_class__, __is_union__ etc) that returns a Boolean integral constant expression giving the value for that trait. The main disadvantage of this approach is that which templates have compiler support has to be fixed in advance; so for example, we can't decide to provide compiler support for is_pointer in order to provide extra support for compiler specific (and non-standard) pointer types. In addition each trait has to be documented twice - once for the template class, and once for its associated operator.
  3. Define a single operator (__traits_of__) which returns an integral constant expression bitmask, containing the traits of that type. Like option 2, this requires duplicating the documentation of each trait.
  4. Provide compiler support for the traits classes directly: when a type traits template is to be instantiated, look first for matching full or partial specialisations of that template, if none is found and the compiler would normally instantiate the primary template, instead generate (and use) a full specialisation based on the compiler's knowledge of the template and its template argument(s). This option allows compiler independence for library vendors, while at the same time allowing compiler vendors to tailor the extent of their support for type traits. This option also has precedent in the way in which SGI's compiler handles their __type_traits class template.

Since it is unclear which (if any) of these options should be adopted, some guidance from both compiler vendors and library authors, on the best way to handle type traits compiler support, would be welcome.


Proposed Text


Type Library

This clause describes components used by C++ programs, particularly in templates, to: support the widest possible range of types, to optimise template code usage, and to 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>

 

Requirements

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.

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.

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.

 

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.

Header <type_traits> synopsis

namespace std{

// Primary type categories:
template <class T> struct is_void;
template <class T> struct is_integral;
template <class T> struct is_float;
template <class T> struct is_array;
template <class T> struct is_pointer;
template <class T> struct is_reference;
template <class T> struct is_member_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;

// 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 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_construct;
template <class T> struct has_nothrow_copy;
template <class T> struct has_nothrow_assign;

} // namespace std


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 const volatile T shall yield the same result.

It is unspecified 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.

Author's Note: Some of these templates require help from the compiler, specifically: is_union and is_class. However, this list can be trimmed down; only one of is_union and is_class need compiler help, since the other can then be implemented by a process of elimination.

template <class T> struct is_void {
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_integral {
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_float {
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_array {
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_pointer {
   static const bool value = implementation_defined; 
};

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.

template <class T> struct is_reference {
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_member_pointer {
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_enum {
   static const bool value = implementation_defined; 
};

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

Author's Note: It may not be immediately obvious how to implement this template without compiler help. In fact enumeration types may be detected by virtue of the fact that they are not integral types, but do have a non-user defined conversion to type int (see boost type_traits implementation[5]).

template <class T> struct is_union {
   static const bool value = implementation_defined; 
};

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

Author's Note: This is the only template in this proposal for which there is no known (or foreseeable) use; except that it can be used in the implementation of is_class, and that its omission would imply that there are some types that do not fit into any of the categories given.

template <class T> struct is_class {
   static const bool value = implementation_defined; 
};

value: defined to be true if T is a class type (3.9.2).

template <class T> struct is_function {
   static const bool value = implementation_defined; 
};

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

[Author's Note- The template is_function can be implemented as follows:

template <class T> struct is_function{
   static const bool value = !is_convertible<T*, const voilatile void*>::value;
};
template <class T> struct is_function<T&>{
   static const bool value = false;
};

Function types (as distinct from function pointers or references) can be introduced either via a typedef or via template argument deduction:

#include <cassert>
#include <type_traits>

typedef int function_type(int);

template <class T>
void deduce_function_type(T& t)
{ assert(std::is_function<T>::value); }

void foo(){}

int main()
{
   assert(std::is_function< function_type>::value); 
   deduce_function_type(foo);
   return 0;
}

- Author's note].

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 const volatile T shall yield the same result.

It is unspecified 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_arithmetic {
   static const bool value = 
      is_integral<T>::value 
      || is_float<T>::value; 
};

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

template <class T> struct is_fundamental{
   static const bool value = 
      is_integral<T>::value 
      || is_float<T>::value 
      || is_void<T>::value; 
};

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

template <class T> struct is_object{
   static const bool value = 
      !(is_function<T>::value 
         || is_reference<T>::value 
         || is_void<T>::value); 
};

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

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; 
};

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

template <class T> struct is_compound{
   static const bool value = 
      is_array<T>::value 
      || is_function<T>::value 
      || is_pointer<T>::value 
      || is_reference<T>::value 
      || is_enum<T>::value 
      || is_union<T>::value 
      || is_class<T>::value 
      || is_member_pointer<T>::value; 
};

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

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.

The template is_const shall have its member value defined to be true for all types const T, the template is_volatile shall have its member value defined to be true for all volatile T. It is unspecified whether the remaining templates ever define their value members to be true, except that the following conditions shall always hold true for all T:

has_nothrow_*<T>::value == has_nothrow_*<remove_bounds<T>::type>::value
has_nothrow_*<T>::value >= has_trivial_*<T>::value
has_trivial_*<T>::value == has_trivial_*<remove_bounds<T>::type>::value
has_trivial_*<T>::value >= is_POD<T>::value
is_POD<T>::value == is_POD<remove_bounds<T>::type>::value
is_POD<T>::value >= (is_scalar<T>::value || is_void<T>::value)

Author's Note: is this the best and/or correct way to specify these relationships? Are volatile qualified types POD's? Section 3.9p10 says clearly that they are - however is a volatile qualifier compatible with applying memcpy to the type as in section 3.9p2?

template <class T> struct is_const{
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_volatile{
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_POD{
   static const bool value = implementation_defined; 
};

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

template <class T> struct is_empty{
   static const bool value = implementation_defined; 
};

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

Author's Note: Although it is unspecified whether there is any T for which is_empty<T>::value is true, in practice it is possible to implement is_empty so that it gives the correct answer, provided that the compiler implements zero sized empty base classes - see the boost reference implementation[5].

template <class T> struct is_polymorphic{
   static const bool value = implementation_defined; 
};

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

template <class T> struct has_trivial_constructor{
   static const bool value = implementation_defined; 
};

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

template <class T> struct has_trivial_copy{
   static const bool value = implementation_defined; 
};

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

template <class T> struct has_trivial_assign{
   static const bool value = implementation_defined; 
};

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

template <class T> struct has_trivial_destructor{
   static const bool value = implementation_defined; 
};

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

Author's Note: Although it is unspecified whether there is any T for which has_trivial_*<T>::value is true, in practice it is possible to implement these templates so that they do give the correct answer with just a little compiler help. For example the SGI compiler provides automatic specialisations of the SGI __type_traits class[7], the boost type traits classes hook into this mechanism, converting SGI style type traits to the boost style[5].

template <class T> struct has_nothrow_construct{
   static const bool value = implementation_defined; 
};

value: defined to be true if the default constructor for T is guaranteed not to throw.

template <class T> struct has_nothrow_copy{
   static const bool value = implementation_defined; 
};

value: defined to be true if the copy constructor for T is guaranteed not to throw.

template <class T> struct has_nothrow_assign{
   static const bool value = implementation_defined; 
};

value: defined to be true if the assignment operator for T is guaranteed not to throw.

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.

Header <type_compare> synopsis

namespace std{

// 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_and_derived;

} // namespace std

Type Relationships

template <class T, class U> struct is_same{
   static const bool value = false; 
};
template <class T> struct is_same<T,T>{
   static const bool value = true; 
};

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

template <class From, class To> struct is_convertible {
   static const bool value = implementation_defined; 
};

value: defined to be true only if type From is implicitly-convertible to type To (4).

template < class Base, class Derived> struct is_base_and_derived {
   static const bool value = is_convertible<Derived*,Base*>::value; 
};

value: defined to be true only if type Base is a base class of type Derived (10). Equivalent to is_convertible<Derived*,Base*>::value.

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.

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: defined to be a type that is the same as T, except that a top level const-qualifier has been added, provided that T is not already const-qualified or a reference or function type.

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

type: defined to be a type that is the same as T, except that a top level volatile-qualifier has been added, provided that T is not already volatile-qualified or a reference or function type.

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

type: defined to be a type that is the same as T, except that top level const- and volatile-qualifiers have been added, provided that T is not already const-volatile-qualified or a reference or function type.

Author's Note: superficially the add_const, add_volatile and add_cv classes are irrelevant, since, for example, add_const<T>::type is the same as T const, for all T (currently this does not apply to function types - but issue 295 addresses this). However the experience from boost is that several users have asked for these templates to be present in the library for the following reasons: (a) Some users find these more explicit - when combining transformation templates in particular, users like the kind of "built in documentation" that these templates provide. (b) Not all users are aware that cv-qualifying a reference is allowed and has no effect, or that cv-qualifying a type that is already cv-qualified is allowed and has no effect. (c) Compilers may emit warnings when cv-qualifying a type that is a reference, or already has a cv-qualifier, these templates can be implemented such that these messages are suppressed in these cases.

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: defined to be a type that is the same as T&.

Author's Note: the add_reference template was one of the original motivations behind the boost type traits library. However the resolution to issue 106 makes the template appear largely redundant. In spite of that add_reference may well be useful in suppressing compiler warnings when inadvertently creating references to references in template code.

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.

Author's Note: there is no add_bounds template, basically because there are several different meanings that could be applied to this. For example what happens when you add bounds to a reference type, and if you add bounds to a pointer should you get an array of pointers, or an array of the thing being pointed to. For these reasons the author believes that it is better to deliberately omit this template, and leave it to users to define their own add_bounds template if they need it, that gives the particular semantics that they require.

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.

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 instance of T.


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.

Acknowledgements

The author is particularly indebted to everyone who as worked on the boost type traits library[5], but also to David Abrahams, Darin Adler, Beman Dawes and Emily Winch for their helpful comments on an early draft of this document.