- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
-
C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The <code>shared_ptr</code> Class
- The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The <u>constexpr</u> Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: <tt>export</tt>, Exception Specification and <tt>register</tt>
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
The Type Traits Library, Part I
Last updated Jan 1, 2003.
The type trait library, which is included in the recently approved TR1, defines a uniform and portable interface for querying, comparing and modifying type traits. In the first part of this series I explain what type traits are, why they are needed and how they are used.
The Motivation for Type Traits
Theoretically, a generic algorithm should be type agnostic. When you sort a sequence of int or double, the compiler uses the same template mold to synthesize the specializations sort<int> and sort<double>, respectively. However, in certain cases the algorithm needs more information about the types of the objects it operates on. These pieces of information, collectively referred to as type traits, are necessary for ensuring the correctness of the algorithm. In other cases, type traits enable the implementor to optimize the algorithm for certain types. A classic example is a copy algorithm that is optimized for POD types. If the collection being copied contains POD objects, the algorithm can use memcpy() instead of copying individual objects by invoking their copy constructor. Similarly, every algorithm defines the categories of its iterators. Thus, while distance() takes two forward iterators as arguments, nth_element takes random access iterators. Compilers can use iterator traits to ensure that the correct iterator category is used.
Type Traits in the Real World
Generic libraries use traits extensively. However, even in user-written code type traits are sometimes necessary. Consider a template function that explicitly invokes the destructors of objects constructed by placement new. If the objects have a trivial destructor, then the destructor invocation can be safely elided. However, to perform this optimization, the template function needs to know whether the objects’ class has a trivial destructor.
The Type Traits Library Design
Type traits are evaluated at compile-time, as are all meta-functions. This property is a major advantage since type traits do not incur runtime overhead. The problem is that unlike ordinary functions that are executed at runtime, type traits have to use artful techniques to compute and report the results at compile time. This constraint led to the design Boost’s type traits library. This library served as the basis for TR1 type traits library.
Type traits, e.g., is_pod, is_pointer, has_virtual_destructor etc. are implemented as distinct classes (each class represents a specific type trait). The trait classes share a unified design, whereby each class inherits from the type true_type if the type being queried has the specified property. Otherwise, the trait class inherits from false_type. The use of base classes to mark the existence/absence of a type trait may seem odd if you haven’t experienced with metaprogramming before. However, you will shortly see that the type traits library has a simple and consistent interface.
Remember: since all type traits classes are derived from true_type or false_type, these two types can be used in generic programming to determine the properties of a given type and introduce optimizations that are appropriate for that case. For instance, to determine whether the template parameter T is void, you use the is_void type trait which is defined like this:
template <typename T>
struct is_void : public false_type{};
template <>
struct is_void<void> : public true_type{};
The primary template represents the default case. It inherits from false_type, meaning: T isn’t void. For example, if T is int or std::string, the primary template will be selected. If however T is void, the specialization which inherits from true_type will be selected instead.
In addition to querying types, 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. We will get to this later, but for the time being suffice it to say that the type traits library enables you to modify the type traits of a certain type, not just query its properties.
Let’s look at a few expressions based on is_pod to observe the interface of trait classes. We already know that is_pod<T> inherits from true_type if T is a POD type; otherwise, is_pod<T> inherits from false_type. For example, is_pod<int> inherits from true_type, whereas is_pod<std::string> inherits from false_type.
type_trait<T>::type
To obtain the base class types true_type and false_type, use the expression type_trait<T>::type. For example
is_pod<char*>::type is the type true_type.
is_pod<std::istream>::type is the type false_type.
type_trait<T>::value
Sometimes you need an integral expression that evaluates as true or false, instead of using types. The type_trait<T>::value returns an such integral expression. For example, is_pod<int (*)(long)>::value is an integral constant expression that evaluates to true, whereas is_pod<std::vector<int>>::value is an integral constant expression that evaluates to false.
type_trait<T>::value_type
To obtain the type of the expressions returned by the expression is_pod<T>::value, use is_pod<T>::value_type:
is_pod<int>::value_type is the type bool.
is_pod<myclass>::value_type is the type bool.
In addition to querying types, 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. In the next part of this series we will look at a concrete example of using type traits to optimize an algorithm and see how to perform type transformation.
