Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++ Type Traits


Generic programming -- that is, writing code that works with any data type meeting a set of requirements -- has become the method of choice for delivering reusable code. However, there are times in generic programming when generic just isn't good enough -- sometimes the differences between types are too great for an efficient generic implementation. This is when the traits technique becomes important. By encapsulating those properties that need to be considered on a type-by-type basis inside a traits class, you can minimize the amount of code that has to differ from one type to another, and maximize the amount of generic code.

For example, when working with character strings, one common operation is to determine the length of a null-terminated string. Clearly, it's possible to write generic code that can do this, but it turns out that there are much more efficient methods available. The C library functions strlen and wcslen, for instance, are usually written in assembler, and with suitable hardware support can be considerably faster than a generic version written in C++. The authors of the C++ Standard Library realized this, and abstracted the properties of char and wchar_t into the class char_ traits. Generic code that works with character strings can simply use char_ traits<>::length to determine the length of a null-terminated string, safe in the knowledge that specializations of char_traits will use the most appropriate method available to them.

Type Traits

Class char_traits is a classic example of a collection of type-specific properties wrapped up in a single class -- what Nathan Myers terms a "baggage class." In the Boost type-traits library, we have written a set of very specific traits classes, each of which encapsulate a single trait from the C++ type system. For example, is a type a pointer or a reference type, or does a type have a trivial constructor, or a const qualifier? The type-traits classes share a unified design. Each class has a single member called value -- a compile-time constant that is true if the type has the specified property, and false otherwise. These classes can be used in generic programming to determine the properties of a given type and introduce optimizations that are appropriate for that case.

The type-traits library also contains a set of classes that perform a specific transformation on a type; for example, they can remove a top-level const or volatile qualifier from a type. Each class that performs a transformation defines a single typedef-member type that is the result of the transformation. All of the type-traits classes are defined inside namespace boost; for brevity, this namespace qualification is omitted in most of the code samples presented here.

Implementation

There are far too many separate classes contained in the type-traits library to give a full implementation here (see the source code in the Boost library for full details). However, most of the implementation is fairly repetitive anyway, so in this article we will give you a flavor for how some of the classes are implemented. Beginning with possibly the simplest class in the library, is_void<T> has a member value that is true only if T is void; see Listing One. Here we have defined the primary version of the template class is_void, and provided a full specialization when T is void.

Listing One
template <typename T> 
struct is_void
{ static const bool value = false; };

template <> 
struct is_void<void>
{ static const bool value = true; };

While full specialization of a template class is an important technique, you sometimes need a solution that is halfway between a fully generic solution, and a full specialization. This is exactly the situation for which the standards committee defined partial template-class specialization. To illustrate, consider the class boost::is_pointer<T>. Here we needed a primary version that handles all the cases where T is not a pointer, and a partial specialization to handle all the cases where T is a pointer; see Listing Two.

Listing Two
template <typename T> 
struct is_pointer 
{ static const bool value = false; };
 
template <typename T> 
struct is_pointer<T*> 
{ static const bool value = true; };

The syntax for partial specialization is somewhat arcane and could easily occupy an article in its own right. Like full specialization, to write a partial specialization for a class, you must first declare the primary template. The partial specialization contains an extra <...> after the class name that contains the partial specialization parameters; these define the types that will bind to that partial specialization, rather than the primary template. The rules for what can appear in a partial specialization are somewhat convoluted, but as a rule of thumb, if you can legally write two function overloads of the form:



void foo(T);

void foo(U);

then you can also write a partial specialization of the form:



template <typename T>

class c{ /*details*/ };

template <typename T>

class c<U>{ /*details*/ };

This rule is by no means foolproof, but it is reasonably simple to remember and close enough to the actual rule to be useful for everyday use.

As a more complex example of partial specialization, consider the class remove_bounds<T>. This class defines a single typedef-member type that is the same type as T but with any top-level array bounds removed. This is an example of a traits class that performs a transformation on a type; see Listing Three.

Listing Three

template <typename T> 
struct remove_bounds
{ typedef T type; };

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

The aim of remove_bounds is this: Imagine a generic algorithm that is passed an array type as a template parameter, remove_bounds provides a means of determining the underlying type of the array. For example, remove_bounds<int[4][5]> ::type would evaluate to the type int[5]. This example also shows that the number of template parameters in a partial specialization does not have to match the number in the primary template. However, the number of parameters that appear after the class name do have to match the number and type of the parameters in the primary template.

Optimized copy

As an example of how the type-traits classes can be used, consider the standard library algorithm copy:



template<typename Iter1, typename Iter2>

Iter2 copy(Iter1 first, Iter1 last, Iter2 out);

Obviously, there's no problem writing a generic version of copy that works for all iterator types Iter1 and Iter2; however, there are some circumstances when the copy operation can best be performed by a call to memcpy. To implement copy in terms of memcpy, all of the following conditions need to be met:

  • Both of the iterator types Iter1 and Iter2 must be pointers.
  • Both Iter1 and Iter2 must point to the same type -- excluding const and volatile qualifiers.
  • The type pointed to by Iter1 must have a trivial assignment operator.

By "trivial assignment operator," we mean that the type is either a scalar type (that is, an arithmetic type, enumeration type, pointer, pointer to member, or const- or volatile-qualified version of one of these types) or:

  • The type has no user-defined assignment operator.
  • The type does not have any data members that are references.
  • All base classes, and all data member objects must have trivial assignment operators.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.