RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Easier C++: An Introduction to Concepts : Page 2

C++0x concepts bring the full power of the Generic Programming paradigm to C++, making templates more expressive, easier to write, and easier to use. Spectacularly poor template error messages are a thing of the past!

A Min(imal) Example
Your journey into concepts starts with the simplest of function templates, min():

template<typename T>
const T& min(const T& x, const T& y) {
  return x < y? x : y;
Despite being simple, the min() function does place some requirements on its template type parameter T. The user must provide a type that has a less-than operator (<) to compare two values of type T, the result of which must be convertible to a Boolean value. Supply a type that has such a less-than operator (like int or std::string) and min() will work; supply a type without a less-than operator (such as std::complex<double>), and min() will fail to instantiate.

Listing 1 shows how to introduce concepts into the min() function template. The min() function template has been augmented with a requires clause, which states the template requirements in terms of concepts, which I'll describe in a moment. In this case, the requirement is LessThanComparable<T>, meaning, "T must meet the LessThanComparable concept's requirements." Other than the addition of the requires clause, the definition of min() is unchanged. However, because min() now has requirements (a.k.a. constraints), it's called a constrained template.

Concepts are entities that describe the operations that a type is expected to have. The beginning of Listing 1 declares the LessThanComparable concept with a single type parameter, T. The body of the concept describes what it means for T to be LessThanComparable. In this case, it means that there is a less-than operator taking two values of type T and returning something that is convertible to bool. You could certainly express other operations in the body of the concept—other operators, member functions, non-member functions, constructors, etc.—but few other operations make sense for LessThanComparable.

Constrained templates, through their use of concepts, describe the contract between the template user and author so that the compiler can verify that both parties abide by the terms of the contract. Next, you'll see how the compiler verifies both sides of the contract.

User's Side of the Contract
To call the min() function, the user must provide a type for T that meets the LessThanComparable concept's requirements. Because C++ integers provide a less-than operator, the call min(17, 42) is a reasonable test. However, this call will fail with an error at the call site (not in the implementation of min()) stating that int is not LessThanComparable. The problem, in this case, is that the compiler does not know what the less-than operator in LessThanComparable actually means, and therefore cannot determine whether the built-in less-than provided for integers agrees with that meaning. Therefore, the user states explicitly that int meets the LessThanComparable concept's requirements through the use of a concept map:

concept_map LessThanComparable<int> { }
Concept maps state that a particular type meets the concept's requirements. When one attempts to use a constrained template like min(17, 42), the compiler searches for a concept map LessThanComparable<int> and will find the concept map defined above, so the call succeeds.

Concept maps play a second role in checking the user's side of the contract, because the concept maps themselves are verified against the body of the concept. In the concept map above, the compiler verifies that the int type has a suitable less-than operator (it happens to be a built-in operator), which returns a type that is convertible to bool. This checking can be seen when one attempts to write an incorrect concept map, for instance:

concept_map LessThanComparable<std::complex<float>> { 
} // error: no operator< for std::complex<float>
Because std::complex<float> does not provide a less-than operator, the compiler will produce an error message stating that this type does not meet the LessThanComparable concept's requirements. Because you can't even define the concept map to state that complex numbers are LessThanComparable, you can't attempt to use the min() function. Most importantly, you never have to look into the actual body of min() to determine that there was an error; the requirements have all of the information you need.

For trivial concepts such as LessThanComparable, the need to write a concept map for every type can become a bit onerous. For this reason, there is a special kind of concept, called an auto concept, for which the compiler will automatically provide concept maps whenever they are needed. If you change the definition of the LessThanComparable concept to the following, users will no longer have to write concept maps for LessThanComparable to call the min() function:

auto concept LessThanComparable<typename T> {
  bool operator<(const T& x, const T& y);

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date