C++ Concepte PDF
C++ Concepte PDF
Douglas Gregor Open Systems Laboratory Indiana University Bloomington, IN 47405 [email protected] Bjarne Stroustrup Department of Computer Science Texas A&M University College Station, TX 77843 [email protected]
Document number: N2081=06-0151 Revises document number: N2042=06-0112 Date: September 10, 2006 Project: Programming Language C++, Evolution Working Group
Contents
1 Introduction 1.1 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Related Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Using Concepts 2.1 Concepts . . . . . . . . . . . 2.1.1 Multi-Type Concepts 2.1.2 Composing Concepts 2.1.3 Associated Types . . 2.2 Concept Maps . . . . . . . . 2.3 Constrained Templates . . . 2.4 Concept-based Overloading 2 3 4 5 5 6 7 8 8 10 13 13 15 15 15 16 18 20 21 22 23
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
3 Proposed Language Features 3.1 New Keywords . . . . . . . . . . . . 3.2 Concepts . . . . . . . . . . . . . . . . 3.2.1 Renements . . . . . . . . . . 3.2.2 Signatures . . . . . . . . . . . 3.2.3 Associated Types, Values, and 3.2.4 Nested Requirements . . . . . 3.2.5 Axioms . . . . . . . . . . . . 3.3 Concept Maps . . . . . . . . . . . . . 1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Template Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
Doc. no: N2081=06-0151 3.3.1 Signature Denitions . . . . . . . . . . . . . . . . 3.3.2 Associated Type, Value, and Template Denitions 3.3.3 Implicit Member Denitions . . . . . . . . . . . . 3.3.4 Renements and Concept Maps . . . . . . . . . . 3.3.5 Concept Map Matching . . . . . . . . . . . . . . Constrained Templates . . . . . . . . . . . . . . . . . . . 3.4.1 Where Clauses . . . . . . . . . . . . . . . . . . . 3.4.2 Inline Requirements . . . . . . . . . . . . . . . . 3.4.3 Type-Checking Constrained Templates . . . . . . 3.4.4 Instantiating Constrained Templates . . . . . . . 3.4.5 Constraint Propagation . . . . . . . . . . . . . . . 3.4.6 Partial Ordering by Where Clauses . . . . . . . . 3.4.7 Late Checking . . . . . . . . . . . . . . . . . . . . Header concepts . . . . . . . . . . . . . . . . . . . . . . 3.5.1 Concept SameType . . . . . . . . . . . . . . . . . 3.5.2 Concept True . . . . . . . . . . . . . . . . . . . . 3.5.3 Concept CopyConstructible . . . . . . . . . . . . . Miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . 3.6.1 Implicit Declaration of Class Members . . . . . . 3.6.2 Default Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 25 27 28 30 32 33 33 36 38 41 42 43 44 45 45 46 46 46 47 47 48 48
3.4
3.5
3.6
Introduction
Note: This document is a minor revision of N2042, which introduces support for rvalue references and corrects many typographical errors. Concepts introduce a type system for templates that makes templates easier to use and easier to write. By checking the requirements that templates place on their parameters prior to template instantiation, concepts can eliminate the spectacularly poor error messages produced by todays template libraries, making them easier to use. On the implementation side, concepts replace a grab-bag of template tricks (including SFINAE, tag dispatching, traits, and some forms of template metaprogramming) with a small set of features designed specically to support the Generic Programming paradigm that underlies many C++ template libraries, thus making it easier for users to develop robust template libraries. At the core of concepts is the idea of separate type checking for templates. Template declarations are augmented with a set of constraints (requirements): if the denition of the template typechecks against these requirements, and a use of a template meets these requirements, then the template should not fail to instantiate. This proposal represents a merger of many dierent design ideas from many dierent people, many of which appear in a series of committee proposals [4,5,6,9,10,11,12]. The vast
majority of this proposal has been implemented in the ConceptGCC [2] compiler. Concepts have been used to upgrade much of the Standard Library to provide better error messages and a cleaner, more robust implementation, illustrating their immediate usefulness to C++ users and their ability to provide excellent backward compatibility with C++03. Concepts have also been used to express more advanced generic libraries, such as the Boost Graph Library.
1.1
Goals
Concepts are intended to improve the C++ template system and make Generic Programming more accessible. Here we lay out our specic goals when designing a concept system for C++0x. Make it easier to write generic code Implementing generic libraries in C++03 is far too complicated due to the large number of template techniques required. Concepts should make it easier to write generic code by making templates simpler and safer, without increasing the amount of code that users need to write. Provide performance as good as plain old templates C++ templates provide exibility while retaining unsurpassed performance, making them ideal for implementing generic, high-performance libraries. The introduction of concepts must not introduce any overhead relative to the existing template mechanism. Support Generic Programming The Generic Programming paradigm, which was behind the development of the Standard Template Library and drives the design of most generic C++ libraries, gives us a solid foundation on which concepts are based. Concepts should fully support Generic Programming, so that C++ can maintain its dominance as the best language for Generic Programming. Support most current template uses Current template usage involves: built-in types and user-dened types concrete types and class hierarchies operations as members, as free standing functions, and as member templates, operations as operators and as named functions, and as function objects parameterization on types, integers, operations, and templates reliance on overloading and conversion Concepts must provide general ways of supporting most of the uses currently considered important for style or performance. Some programming styles/techniques, such as template meta-programming, may not be obvious candidates for concept support, but it should be possible to gain some of the benets of concepts where complete support is possible.
Maintain backward compatibility We need to retain the current meaning of templates, so that existing programs will continue to compile. More interesting, however, is that we need to enable backward compatibility in C++ template libraries as those libraries are converted to use concepts. As an example, the C++ Standard Library, when upgraded with concepts, should be backward-compatible with the C++03 Standard Library. Have a comprehensible compilation model Most of the users of concepts will not have PhDs and will not read anything with Greek letters. It should be possible for a reasonably bright programmer to correctly imagine what the memory layout of his data structures are, what functions are called, and what code is replicated during instantiation. Concepts is a language feature, not a confederation of features The various concept mechanisms must t together seamlessly. There must be no seams where something can be expressed in several ways (each reecting a focus on a single part) or where something cant be done because two parts cant quite exchange needed information.
1.2
Status
This document is in draft status. It contains many typos and errors, and lacks much introductory information that would aid understanding. At present, it is best viewed as a reference manual for readers that already understand concepts. With future versions of this document, we hope to improve the presentation to provide both an introductory view and a language-technical view of concepts. At present, the best introduction to concepts is given in [3]. Preprints are available on the rst authors home page at http://www.generic-programming.org/~dgregor. We hope this document will help readers use concepts, as currently implemented in ConceptGCC, to gain experience with concepts. Most importantly, we also hope for help in our search for simplications of the concept-related language features and in our search for more elegant ways of expression concepts and their uses. Among the most active such areas are: 1. Can requirements on operations best be dened in terms of signatures (as used in this document) or as Use patterns [12] (as currently being implemented to allow direct comparison)? 2. Is it possible to simplify the renement and nested requirements (possibly unifying the language facilities supporting those ideas)? 3. Is it possible to improve, simplify, or generalize the shortcut rule for qualifying concept members with their template argument name (see alternative in Section 3.4.2)? 4. Are concepts still expressive enough if || constraints cannot be used (see Section 3.4.1)? 5. What is the most natural semantics for unqualied name lookup within constrained templates (see Section 3.4.3 and its alternative)?
1.3
Related Documents
This document supercedes all previous concepts proposals [6, 7, 9, 10, 11, 12]. In addition to this proposal, which denes the language features required to support concepts, we have also provided a series of proposals that introduce concepts into the C++0x Standard Library. While not complete at this time, these proposals serve three important purposes. First, they provide many real-world examples of the denition and use of concepts, and can be used as a companion to this proposal. While we endeavor to keep examples short and simple within this document, the documents describing a concept-enhanced Standard Library contain actual, working denitions with full details. Second, these proposals illustrate that concepts can model existing, non-trivial uses of templates while providing excellent backward compatibility. Finally, concepts as a language feature will not gain widespread acceptance without a concept-enabled Standard Library. The Standard Library will give users their rst exposure to concepts in C++0x, providing the canonical examples from which users will learn how best to utilize these new features. If we (the committee) dont use concepts, who will? The following companion proposals describe changes to the C++0x Standard Library to introduce complete support for concepts: Concepts for the C++0x Standard Library: Approach [N2036=06-0106] Concepts for the C++0x Standard Library: Introduction [N2037=06-0107] Concepts for the C++0x Standard Library: Utilities (Revision 1) [N2038=06-0108] Concepts for the C++0x Standard Library: Containers [N2085=06-0155] Concepts for the C++0x Standard Library: Iterators (Revision 1) [N2083=06-0153] Concepts for the C++0x Standard Library: Algorithms (Revision 1) [N2084=06-0154] Concepts for the C++0x Standard Library: Numerics [N2041=06-0111]
Using Concepts
Concepts essentially provide a type system for templates, allowing the denition of templates to be type-checked separately from their uses. The core feature of concepts, therefore, is to provide a way to state what behaviors template parameters must have, so that the compiler can check them. For instance, consider the min() function from the Standard Library:
template<typename T> const T& min(const T& x, const T& y) { return x < y? x : y; }
When can we use min() with any type T that has a less-than operator taking two values of type T and returning some value convertible to bool. So long as those requirements are met, min<T>() will instantiate properly. These requirements cannot be expressed in C++, so they
are typically expressed in documentation as concepts. See, e.g., the SGI Less Than Comparable concept documentation at http://www.sgi.com/tech/stl/LessThanComparable.html. Concepts allow us to express these concept requirements directly in the min() template:
template<LessThanComparable T> const T& min(const T& x, const T& y) { return x < y? x : y; }
Here we have used concepts to place a requirement on the template type parameter T. Instead of stating that T is an arbitrary type via typename, we state that it is a LessThanComparable type, meaning that min() will only accept parameters whose types meet the requirements of the LessThanComparable concept. From the users point of view, the concept-based min() is almost identical to its predecessor: for any LessThanComparable type, it works in the same way. However, there is a large dierence when min() is called with a type that is not LessThanComparable: instead of failing to instantiate min() (when no suitable operator< can be found), the error is detected at the call to min() when the LessThanComparable requirements cannot be satised. This early detection of errors is the reason that concepts improve error messages, since we no longer need to produce an instantiation stack or direct users to code inside a librarys implementation.
2.1
Concepts
How does the LessThanComparable concept describe its requirements? In many cases, we need only list the signatures of functions and operators we want to have within the denition of the concept. Here is the denition of LessThanComparable:
auto concept LessThanComparable<typename T> { bool operator<(T, T); };
When dening a concept, we use the keyword concept followed by the name of the concept and a template parameter list. In this case, the LessThanComparable concept only has one parameter, the type for which we want operator< dened. Inside the body of the concept, we list the declarations that we expect to have, i.e., an operator< that takes two T parameters and returns a bool. The auto specier means that any type which has a suitable operator< will be considered LessThanComparable; if omitted, the user will have to explicitly state that her types meet the requirements of the concept using a concept map (see Section 2.2). Concept signatures can describe many dierent kinds of requirements for functions, constructors, operators, member functions, member function templates, etc. The following concept, Regular, illustrates many of these requirements. The Regular concept applies to well-behaved types that can be constructed, copied, compared, etc.
auto concept Regular<typename T> { T:: T(); T:: T(const T&); T::T(); T& operator=(T&, const T&); // // // // default constructor copy constructor destructor copy assignment
2.1.1
Concepts can describe requirements on multiple types simultaneously. For instance, we often need to express that two types arent necessarily equal, but one can be converted to another. This requirement can be expressed with a two-parameter concept Convertible:
auto concept Convertible<typename T, typename U> { operator U(const T&); };
To use this concept, we need to employ the general form of describing the constraints on a template, called a where clause. Where clauses can contain any number of concept constraints that augment the constraints placed in the template header. For instance, we can dene a function template that converts from one type to another:
template<typename U, typename T> where Convertible<T, U> U convert(const T& t) { return t; }
convert() can be used for any valid conversion, e.g., convert<oat>(17) is valid (int is convertible to oat) but convert<int>(17.0) is not (a oat is not convertible to an int pointer). Well come back to where clauses later. With multi-type concepts, we have the tools to express rudimentary iterator concepts, like those in the Standard Library. For instance, we could make Iterator a two-parameter concept, one for the iterator type itself and one for the value type of the iterator:
concept InputIterator <typename Iter, typename Value> { Iter :: Iter (); // default constructor Iter :: Iter ( const Iter &); // copy constructor Iter :: Iter (); // destructor Iter & operator=(Iter&, const Iter&); // copy assignment Value operator(const Iter&); // dereference Iter & operator++(Iter&); // pre- increment Iter operator++(Iter&, int); // post- increment bool operator==(const Iter&, const Iter&); // equality comparison bool operator!=(const Iter&, const Iter &); // inequality comparison void swap(Iter &, Iter &); // swap }
2.1.2
Composing Concepts
Looking at the InputIterator and Regular concepts, there is a lot of overlap. In fact, every requirement in the Regular concept is also a requirement on the Iter type parameter to the InputIterator concept! What we really want to say is that the Iter type of the InputIterator concept is Regular, which we can do with a nested requirement. Nested requirements are like where clauses, but they go inside the body of the concept as follows:
concept InputIterator<typename Iter, typename Value> { where Regular<Iter>; Value operator(const Iter&); // dereference Iter& operator++(Iter&); // pre-increment Iter operator++(Iter&, int); // post-increment }
Any concept requirements can be composed in this manner. Sometimes, however, there is a more fundamental, hierarchical relationship between two concepts. For instance, every RandomAccessIterator is a BidirectionalIterator, every BidirectionalIterator a ForwardIterator, and every ForwardIterator an InputIterator. Concept renement allows us to express these hierarchical relationships using a syntax akin to inheritance:
concept ForwardIterator<typename Iter, typename Value> : InputIterator<Iter, Value> { // no syntactic dierences, but adds the multi-pass property }
Concept renement has one particularly important property: every type that meets the requirements of the rening concept (ForwardIterator) also meets the requirements of the rened concept (InputIterator), so my forward iterators can be used in an algorithm that requires input iterators, such as nd(), above. When composing concepts, should you use nested requirements or renement? If there is a direct hierarchical relationship, use renement; otherwise, use nested requirements. 2.1.3 Associated Types
Standard Library acionados will note that the InputIterator concept provided is overly simplied. In particular, the value type of an iterator (represented by the parameter Value) is only one of four extra types for an iterator. To be more precise, we really should declare InputIterator as:
concept InputIterator<typename Iter, typename Value, typename Reference, concept InputIterator<typename Pointer, typename Dierence> { where Regular<Iter>;
Unfortunately, these extra concept parameters come with a cost: every time we use the InputIterator concept, we need to declare template parameters for every concept parameter. This new, more proper formulation of InputIterator forces a lot of complexity into our previously-simple nd() algorithm:
template<typename Iter, Regular V, typename R, typename P, typename D> where InputIterator<Iter, V, R, P, D> Iter nd(Iter rst, Iter last, const V& value) { while (rst != last && rst != value) ++rst; return rst; }
Associated types allow us to declare auxiliary types like Reference, Pointer, Dierence and even Value inside the concept body, minimizing the number of concept parameters and simplifying algorithms. With associated types, we can make InputIterator a single-parameter concept and keep the denition of nd() simple:
concept InputIterator<typename Iter> { typename value type; typename reference; typename pointer; typename dierence type; where Regular<Iter>; where Convertible<reference type, value type>; reference operator(const Iter&); // dereference Iter& operator++(Iter&); // pre-increment Iter operator++(Iter&, int); // post-increment // ... } template<InputIterator Iter> where Regular<Iter::value type> Iter nd(Iter rst, Iter last, const Iter::value type& value) { while (rst != last && rst != value) ++rst; return rst; }
There is some new syntax in this example. We have moved the InputIterator requirement back into the template header (InputIterator Iter). Then we state that the value type of Iter, written Iter::value type (no typename required!) is Regular. Finally, we require that the
10
third parameter to nd() be of type Iter::value type. Note how associated types have helped us simplify the nd() template, because we only need to refer to the associated types that we are interested in. Associated types can be used for many reasons, but much of the time they are used to express the return types of signatures that are otherwise unknown. For instance, the Standard Librarys transform() operation accepts a binary function, for which we could write the following concept:
auto concept BinaryFunction<typename F, typename T1, typename T2> { typename result type; result type operator()(F&, const T1&, const T2&); };
The operator() signature in this concept says that a value of type F can be invoked with two arguments of type T1 and T2, respectively. The BinaryFunction concept does not specify what operator() must return, so it contains an associated type that gives a name to this return value (result type) but places no requirements on the associated type. Many uses of BinaryFunction will, of course, introduce their own requirements on the type. For instance, the following declaration of transform() uses result type as the value type of an OutputIterator:
template<InputIterator InIter1, InputIterator InIter2, typename OutIter, typename BinOp> where BinaryFunction<BinOp, InIter1::reference, InIter2::reference> && OutputIterator<OutIter, BinaryFunction<BinOp, InIter1::reference, InIter2::reference>::result type> OutIter transform(InIter1 rst1, InIter1 last1, InIter2 rst2, OutIter result, BinOp binary op);
By using associated types to describe return types for which we have no requirements, we also open the door for renements to add requirements on those return types. For instance, a binary predicate is just a binary function whose return type can be interpreted as a boolean value:
auto concept BinaryPredicate<typename F, typename T1, typename T2> : BinaryFunction<F, T1, T2> { where Convertible<result type, bool>; };
The reader may recognize that we use associated types in the same places where existing C++ libraries use traits: in fact, concepts and associated types replace the need for traits with a simpler, safer mechanism. Associated types can even be accessed with a more general syntax that is reminescent of traits, e.g., InputIterator<Iter>::value type (again, no typename), which is necessary for multi-type concepts.
2.2
Concept Maps
Concepts describe the interfaces required for generic algorithms and data structures to work properly, but sometimes it isnt clear whenor howcertain types meet the interface requirements of a concept. Concept maps allow users to specify when their types meet the
11
requirements of a concept (required when the concept is not marked auto), but also allow one to adapt the syntax of existing types to the syntax expected by the concept without changing the denition of the types. For instance, we can make all char pointers into InputIterators with a simple concept map:
concept map InputIterator<char> { typedef char value type ; typedef char& reference ; typedef char pointer ; typedef std:: ptrdi t dierence type ; };
This concept states that chars are InputIterators (allowing us to call algorithms requiring InputIterators with character pointers) and provides denitions for each of the associated types in the concept. The remaining requirementsdefault constructibility, copy constructibility, increment, dereference, etc.will be implicitly dened by the compiler. If the implicit denitions of certain operations will not work or are incorrect, concept maps may provide their own function denitions instead. This syntax adaptation allows us to view types dierently when seen through concepts, so that a type can expose many dierent interfaces without having those interfaces clash. You can think of concept maps as a pair of colored glasses that changes how you see types without changing the actual type. For instance, we could imagine looking at an integer as an iterator, where the dereference operator is the identity function:
concept map InputIterator<int> { typedef int value type; typedef int reference; typedef int pointer; typedef int dierence type; int operator(int x) { return x; } };
Looking at int through our InputIterator glasses, we see that incrementing the int moves to the next value, dereferencing an int return itself (so the sequence just produces integer values), and all of the other iterator requirements are met. We can now use integers when calling Standard Library algorithms, e.g., to write a series of values through cout:
copy(1, 17, std::ostream iterator<int>(std::cout, )); // prints: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Like many other constructs in C++, concept maps can also be templated. Since all pointers are InputIteratorss (not just character pointers!), we should instead write a concept map template:
template<typename T> concept map InputIterator<T> { typedef T value type ; typedef T& reference ; typedef T pointer ;
12
The real power of concept maps comes in when we combine concept map templates with syntax adaptation, composing concept maps so that we can look at data types through a series of lenses piled on top of one another. Consider, for instance, a concept Stack, which only allows one to push values, pop values, query the top value, and determine when the stack is empty:
concept Stack<typename X> { typename value type; void push(X&, const value type&); void pop(X&); value type top(const X&); bool empty(const X&); };
The Stack concept can be implemented by many dierent data structures, among them is std::vector. To do so, we need only transform some syntax:
template<typename T> concept map Stack<std::vector<T> > { typedef T value type; void push(std:: vector <T>& v, const T& void pop(std:: vector <T>& v) T top(const std:: vector <T>& v) bool empty(const std::vector <T>& v) }; x) { v.push back(x); } { v. pop back(); } { return v. back(); } { return v. empty(); }
Now, every std::vector is a Stack. What about std::list and std::deque? These other data structures provide the same Stack-like members as std::vector, so we would need to write identical concept maps for them. Shouldnt we be able to write concept maps that support all of these containers? The containers std::vector, std::list, and std::deque shared much of their interface. In fact, most of their interface is described by the BackInsertionSequence concept, an excerpt from which is shown below:
concept BackInsertionSequence<typename X> { typename value type = X::value type; void X::push back(const value type&); void X::pop back(); value type& X::back(); const value type& X::back() const; bool X::empty() const; };
Since we can implement a Stack with any data structure that provides suitable push back, pop back, back, and empty functions (like std::vector, std::list, and std::deque), we can implement a Stack with any data structure that meets the requirements of a BackInsertionSequence. The concept map would be written as follows:
13
How is an std::list a Stack? Well, we know that std::list is a BackInsertionSequence, because that is dened by the standard. The concept map above says that every type X that is a BackInsertionSequence is also a Stack, with some adaptation to the syntax. In the glasses analogy, we see through the Stack concept which bends the syntax to that of the BackInsertionSequence concept, which we see through to the actual implementation of std::list. By templating concept maps in terms of other concepts, we allow users to stack lenses together and allow one to seamlessly bridge from one set of concepts into another.
2.3
Constrained Templates
Templates that use concepts are actually of a new kind of template, called a constrained template. Constrained templates are templates that contain a concept constraints on their template parameters. Constrained templates are fully type-checked at the time of denition, so that errors in the template denition will be caught early. For instance, say we tried to write a max() function as follows:
template<LessThanComparable T> const T& max(const T& x, const T& y) { return x > y? x : y; }
This denition of max() is ill-formed because it uses the > operator in its body. Without concepts, this error would only be detected when a user tries to call max() with a type that provides < but not >. With concepts, however, this template denition is ill-formed: when the function body is initially parsed, the compiler will attempt to nd any > operator that takes two parameters of type T. The compiler will search inside the LessThanComparable concept (which provides a < operator, only) and the global scope, but there is no such operator. Thus, the denition of the template is ill-formed. On the other hand, when type-checking the min() template, the search for an operator< to satisfy x < y will match the operator< in LessThanComparable. And, since that operator< returns a bool value, the min() will type-check properly. By type-checking both the denitions and uses of a template against the same set of requirements, concepts can eliminate nearly all instantiation-time failures.
2.4
Concept-based Overloading
Some algorithms can be implemented in several dierent ways depending on the capabilities of their input types. The canonical example of this ability is the advance() algorithm from the
14
Standard Library, which moves an iterator i forward n steps. For input iterators, advance() can only step the iterator forward n times, requiring a linear number of operations. With bidirectional iterators, advance() can move either forward (n > 0) or backward (n < 0), but still requires a linear number of operations to do so. Random access iterators are the most powerful iterators, because they can jump any number of steps in a single operation. Concept-based overloading allows us to provide multiple versions of the same algorithm, each with dierent concept requirements, and ensures that the most specic algorithm will be picked when called. Using concept-based overloading is as simple as writing down the dierent implementations of the algorithm:
template<InputIterator Iter> void advance(Iter& x, Iter::dierence type n) { while (n > 0) { ++x; --n; } } template<BidirectionalIterator Iter> void advance(Iter& x, Iter::dierence type n) { if (n > 0) while (n > 0) { ++x; --n; } else while (n < 0) { --x; ++n; } } template<RandomAccessIterator Iter> void advance(Iter& x, Iter::dierence type n) { x += n; }
Now, if we call advance with a pointer and an oset, the compiler will select the third (most ecient) algorithm, because pointers are RandomAccessIterators. On the other hand, if we call advance with an iterator into an std::list, the compiler will select the second algorithm, because the third algorithm is not supported. The behavior of the advance() function above should look familiar: it does the same thing as advance() in the C++03 Standard Library, but instead of implementing concept-based overloading using template trickse.g., tag dispatching or SFINAEconcepts provide the precise feature we need to solve this problem. Concept-based overloading is actually a form of specialization, aecting the partial ordering rules for function and class templates alike. Thus, one could provide dierent versions of a class template based on the concept requirements that its parameters meet:
template<EqualityComparable T> class dictionary { // slow, linked-list implementation }; template<LessThanComparable T> where !Hashable<T> class dictionary<T> { // balanced binary tree implementation }; template<Hashable T> class dictionary<T> {
15
For this dictionary template , well select among three dierent implementationsa linked list, a balanced binary tree, or a hash tablebased on what operations the type T supports. N aively writing down the three operations would leave us with an ambiguity: what if a type T was both LessThanComparable and Hashable? To resolve the ambiguity, we use a negative constraint, which states that the second version of dictionary (for balanced binary trees) will only be selected if the type T is not Hashable, thereby preferring the hash table implementation when possible. Thus, concept-based overloading and specialization allows multiple versions of templates to provide the same capabilities in dierent ways, and negative constraints help the decision-making process by resolving ambiguities.
This section describes concepts, their syntax, semantics, and related features. Written in a style part-way between the Annotated C++ Reference Manual [1] and actual proposed text, it is intended as a reference manual for users and implementors alike. Future proposals will rene and clarify this text, with separate documents containing proposed text.
3.1
New Keywords
This proposal introduces ve new keywords: concept, concept map, where, axiom, and late check. All of these keywords will also be reserved words.
3.2
Concepts
declaration : concept-denition concept-denition : autoopt concept identier < template-parameter-list > renement-clauseopt where-clauseopt concept-body ;opt concept-body : { requirement-specicationopt } requirement-specication : signature requirement-specicationopt associated-req requirement-specicationopt nested-req requirement-specicationopt axiom-denition requirement-specicationopt concept-id : template-id
16
Concepts describe an abstract interface that can be used to constrain templates. Concepts state certain syntactic and semantic requirements on a set of template type, non-type, and template parameters. When used to describe the interface of a constrained template, the requirements have two roles. First, when a constrained template is dened, the requirements of the concept act as declarations, ensuring that the template body is fully type-checked. Second, when a constrained template is used, the template arguments provided to the template must meet the requirements of the concept. By playing these two roles, concepts permit nearly-perfect separate type checking for templates. 1. A concept-id is a template-id whose template-name refers to the name of a concept. [Example: CopyConstructible<int> is a concept-id if CopyConstructible is a concept.] 2. A concept without a preceding auto specier is an explicit concept. When a concept map is required for an explicit concept, it must be found through normal concept map matching ( 3.3.5). 3. A concept with a preceding auto specier is an implicit concept. When a concept map is required for an implicit concept but no concept map can be found through normal concept map matching ( 3.3.5), it shall be implicitly generated. 4. Concepts shall only be dened at namespace or global scope. 5. The template-parameter-list of a concept shall not contain any requirements specied in-line ( 3.4.2). 6. The where clause of a concept places extra requirements on the concept parameters that must be satised by the arguments. It is functionally equivalent to nested requirements in concepts (3.2.4). 3.2.1 Renements
renement-clause : : renement-specier-list renement-specier-list : renement-specier renement-specier , renement-specier-list renement-specier : ::opt nested-name-specier opt concept-id Renements specify an inheritance relationship among concepts. Concept renement inherits all requirements, so that the requirements of the rening concept are a superset of the requirements of the rened concept.. Thus, when a concept B renes a concept A, every set of template arguments that meets the requirements of B also meets the requirements of A. [Example: In the following example, ForwardIterator renes InputIterator. Thus, any type that meets the requirements of ForwardIterator (say, int) also meets the requirements of InputIterator.
17
end example ] 1. Name lookup into a concept also searches all rened concepts for the name. [Example:
concept InputIterator<typename Iter> { typename dierence type; }; concept RandomAccessIterator<typename Iter> : InputIterator<Iter> { dierence type operator-(Iter x, Iter y); // okay };
end example] 2. Names declared by a concept do not hide names declared in the concepts it renes. A name that refers to an associated type within a concept or any of its rened concepts refers to the same associated type in all rened concepts, i.e., there are no ambiguous associated type names. A name that refers to a signature within a concept or any of its rened concepts refers to an overload set consisting of all signatures declared with that name in the concept and all of its renements. A name shall not refer to both an associated type and a signature. [Example:
concept A<typename T> { typename result type; result type operator+(T,T); }; concept B<typename U> { typename result type; result type operator+(U); }; concept C<typename V> : A<V>, B<V> { // operator+ refers to overload set {A<V>::operator+, B<V>::operator+} // result type refers to the (unique) associated type result type, even // though it has beed declared in both A and B. }; concept D<typename X> { void result type(X); }; concept E<typename Y> : C<Y>, D<Y> { // error: result type is ambiguous // ... };
end example]
18
3. The concept-id s refered to in the renement clause shall correspond to fully dened concepts. [Example: The following example is ill-formed because C is not fully-dened prior to the renement clause.
concept C<typename T> : C<vector<T>> {/ ... / }; // error: recursive renement
end example] 4. A concept-id in the renement clause shall not refer to any associated types. 5. A concept-id in the renement clause shall refer to at least one of the template parameters of the concept being dened. [Example: The following example is ill-formed because it attempts to rene from a concrete concept map.
concept InputIterator<typename Iter> : Incrementable<int> { / ... /}; // error
signature : defaultopt simple-declaration defaultopt function-denition defaultopt template-declaration Signatures describe functions, member functions, or operators that can be used by constrained templates. Signatures take the form of function declarations, which are present within the denition of a constrained template for the purposes of type-checking. Concept maps for a given concept must provide, either implicitly or explicitly, denitions for each signature in the concept ( 3.3.1). 1. Signatures can specify requirements for free functions and operators. [Example:
concept C<typename T> { bool operator==(T, T); bool operator!=(T, T); T invert(T); };
end example] 2. Signatures shall specify requirements for operators as free functions, even if those operators cannot be overloaded outside of a class. [Example:
concept Convertible<typename T, typename U> { operator U(T); T::operator U() const; // error: cannot specify requirement for member operator };
end example]
19
3. Signatures can specify requirements for member functions, including constructors and destructors. [Example:
concept Container<typename X> { X::X(int n); X::X(); bool X::empty() const; };
end example] 4. Signatures can specify requirements for templated functions and member functions. [Example:
concept Sequence<typename X> { typename value type; template<InputIterator Iter> where Convertible<InputIterator<Iter>::value type, Sequence<X>::value type> X::X(Iter rst, Iter last); };
end example] 5. Concepts may contain overloaded signatures, but signatures shall have distinct types. [Example:
concept C<typename X> { void f(X); void f(X, X); // okay void f(X); // error: redeclaration of function f };
end example] 6. All arguments to signatures are passed by reference. When substituting template arguments into a signature parameter, if the type T of the resulting signature parameter is not a reference type, it will be transformed into T const&. [Example:
concept C<typename X> { void f(X); }; struct Y {}; concept map C<Y> { void f(const Y&); // implicitly declared }; concept map C<Y&&> { void f(Y&&); // implicitly declared };
end example]
20
7. Signatures may have a default implementation. This implementation will be used when implicit generation of an implementation fails ( 3.3.3). [Example:
concept EqualityComparable<typename T> { bool operator==(T, T); default bool operator!=(T x, T y) { return !(x == y); } }; class X{}; bool operator==(const X&, const X&); concept map EqualityComparable<X> { }; // okay, operator!= uses default
end example] Signatures with a default implementation must be preceded by the default keyword. 8. Signatures can be identied via qualied names, but their addresses cannot be taken. [Example:
concept C<typename T> { void twiddle(T); }; template<C T> void f(T x) { C<T>::twiddle(x); // okay, same as twiddle(x) &C<T>::twiddle; // error: cannot take the address of a signature };
end example] 9. Rvalue reference parameters in a signature shall only bind to rvalues. Note that this rule diers from the binding of rvalue reference parameters to normal functions. [Example:
concept MoveConstructible<typename T> { T::T(T&&); }; template<MoveConstructible T> void foo(T& x) { T y(x); // error: cannot bind lvalue reference x to an rvalue reference parameter }
associated-req : template-parameter ;
21
Associated types, values, and templates are auxiliary implicit parameters used in the description of a concept. They are like template parameters, in the sense that they vary from one use of a concept to another, and nothing is known about them unless constraints are explicitly placed on them via a where clause. Associated types are by far the most common, because they are often used to refer to the return types of signatures. [Example:
concept Callable1<typename F, typename T1> { typename result type; result type operator()(F, T1); };
end example] 1. Associated types, values, and template parameters may be provided with a default value. This default value will be used in a concept map when no corresponding denition is provided ( 3.3.2). [Example:
concept Iterator<typename Iter> { typename dierence type = int; }; concept map Iterator<int> { }; // okay, dierence type is int
end example] 2. The default value of associated types, values, and templates shall only be type-checked when a concept map implicitly denes the associated type. 3. Associated values are integral constant expressions. 3.2.4 Nested Requirements
nested-req : where-clause ; Nested requirements place additional concepts on the template parameters and associated types, values, and templates of a concept. Nested requirements have the same form and behavior as where clauses for constrained templates. 1. Nested requirements can be used to constrain associated types, values, and templates in a concept. [Example:
concept Iterator<typename Iter> { typename dierence type; where SignedIntegral<dierence type>; };
end example] 2. Inline requirements (3.4.2) may be used to dene associated types. [Example:
22
axiom-denition : axiom identier ( parameter-declaration-clause ) axiom-body axiom-body : { axiom-seq opt } axiom-seq : axiom axiom-seq opt axiom : expression-statement if ( condition ) expression-statement Axioms allow the expression of the semantics required by concepts. Although axioms must be type-checked at the time of denition, it is unspecied whether axioms have any eect on the semantics of the program. [Example:
concept Semigroup<typename Op, typename T> { T operator()(Op, T, T); axiom Associativity(Op op, T x, T y, T z) { op(x, op(y, z)) == op(op(x, y), z); } }; concept Monoid<typename Op, typename T> { T identity element(Op); axiom Identity(Op, T x) { op(x, identity element(op)) == x; op(identity element(op), x) == x; } };
end example] 1. The equality (==) and inequality (!=) operators are provided for the purposes of type-checking axioms. The following two declarations are considered to be in scope for type-checking axioms:
template<typename T> bool operator==(const T&, const T&); template<typename T> bool operator!=(const T&, const T&);
[Example:
23
end example] 2. Where axioms state the equality of two expressions, implementations are permitted to replace one expression with the other. [Example:
template<typename Op, typename T> where Monoid<Op, T> T identity(const Op& op, const T& t) { return op(t, identity element(op)); // can compile as return t; }
end example] 3. Axioms can state conditional semantics using if statements. When the condition can be proven true, implementations are permitted to apply program transformations based on the expression-statement. [Example:
concept PartialOrder<typename Op, typename T> { bool operator()(Op, T, T); axiom Reexivity(Op op, T x) { op(x, x) == true; } axiom Antisymmetry(Op op, T x, T y) { if (op(x, y) && op(y, x)) x == y; } axiom Transitivity(Op op, T x, T y, T z) { if (op(x, y) && op(y, z)) op(x, z) == true; } }
end example]
3.3
Concept Maps
declaration : concept-map-denition template-declaration : template < template-parameter-list > where-clauseopt concept-map-denition concept-map-denition : concept_map concept-id { concept-map-member-specicationopt } ;opt concept-map-member-specication : simple-declaration concept-map-member-specicationopt function-denition concept-map-member-specicationopt template-declaration concept-map-member-specicationopt
24
Concept maps describe how a set of template arguments map to the syntax of concept. Whenever a constrained template is used, there must be a concept map corresponding to each concept-id requirement in the where clause ( 3.4.1). This concept map may be written explicitly, instantiated from a concept map template, or generated implicitly (for an implicit concept). [Example:
class student record { public: string id; string name; string address; }; concept map EqualityComparable<student record> { bool operator==(const student record& a, const student record& b) { return a.id == b.id; } }; template<EqualityComparable T> void f(T); f(student record()); // okay, have concept map EqualityComparable<student record>
end example] 1. Concept maps shall provide, either implicitly ( 3.3.3) or explicitly ( 3.3.1, 3.3.2), denitions for every signature and associated type, value, and template requirement ( 3.2.2, 3.2.3) in the corresponding concept or its renements. [Example:
concept C<typename T> { T f(T); }; concept D<typename T> : C<T> { }; concept map D<int> { int f(int); // okay: matches requirement for f in concept C };
end example] 2. Concept maps shall be dened in the same namespace as their corresponding concept. 3. Concept maps shall not contain extraneous denitions that do not match any requirement in the corresponding concept or its renements. [Example:
concept C<typename T> { }; concept map C<int> { int f(int); // error: no requirement for function f };
end example] 4. At the point of denition of a concept map, all nested requirements ( 3.2.4) of the corresponding concept shall be satised. A concept map for which a nested requirement is not satised is ill-formed. [Example:
25
end example] 5. The denition of a concept map asserts that the axioms ( 3.2.5) dened in the corresponding concept (and its renements) hold. It is unspecied whether these axioms have any eect on program translation. 6. If a concept map is provided, then that concept map shall me declared before the corresponding concept-id is required. [Example:
auto concept EqualityComparable<typename T> { bool operator==(T, T); } template<EqualityComparable T> bool all equal(T x, T y, T z) { return x == y && y == z; } all equal(3.1, 3.101, 2.999); concept map EqualityComparable<double> { // error: concept-id already used bool operator==(double x, double y) { return fabs(x-y) <= 0.001; } }
Signatures ( 3.2.2) are satised by function denitions within the body of a concept map. These denitions can be used to adapt the syntax of the concept arguments to the syntax of the type. [Example:
concept Stack<typename S> { typename value type; bool empty(S); void push(S&, value type);
26
end example] 1. A function declaration in a concept map matches a signature of the same name when the types of the functions are equivalent after substitution of concept arguments. [Example:
concept C<typename X> { void f(X); }; concept map C<int> { void f(int) { } };
end example] 2. All arguments to functions in a concept map are passed by reference. Each parameter type T in a signature denition will be transformed into T const& prior to matching functions to signatures. [Example:
concept C<typename X> { void f(const X&); void g(const X&); void h(X); }; concept map C<int> { void f(const int&) { } // okay: exact match void g(int) { } // okay: parameter becomes const int& void h(int) { } // okay: parameters become const int& };
end example] 3. Functions declared within a concept map may be dened outside the concept map, in a separate translation unit. [Example:
27
end example] 4. Function templates declared within a concept map match a signature template when the signature template is at least as specialized as the function template. [Example:
concept C<typename X> { typename value type; template<ForwardIterator Iter> where Convertible<Iter::value type, value type> void X::X(Iter rst, Iter last); }; concept map C<MyContainer> { typedef int value type; template<InputIterator Iter> where Convertible<Iter::value type, int> void X::X(Iter rst, Iter last) { ... } // okay: signature is more specialized };
Denitions in the concept map provide types, values, and templates for the implicit parameters of concepts ( 3.2.3). These denitions must meet the nested requirements ( 3.2.4) stated in the body of the concept. 1. Associated type requirements are satised by type denitions in the body of a concept map. [Example:
concept ForwardIterator<typename Iter> { typename dierence type; } concept map ForwardIterator<int> { typedef ptrdi t dierence type; };
28
2. Associated value requirements are satised by variable declarations, which must be provided with an initializer, in the body of a concept map. [Example:
concept Tuple<typename T> { int length; } template<typename T, typename U> concept map ForwardIterator<std::pair<T, U> > { int length = 2; };
end example] 3. Associated template requirements are satised by class template declarations in the body of the concept map. [Example:
concept Allocator<typename Alloc> { template<class T> class rebind; } template<typename T> concept map Allocator<my allocator<T> > { template<class U> class rebind { public: typedef my allocator<U> type; }; };
When the requirements of a concept and its renements are not satised by the denitions in the body of a concept map ( 3.3.1, 3.3.2), default denitions are implicitly dened from the requirements and their default values. 1. Implicitly-dened functions generated for signatures contain a single statement, which is either a return statement containing a use of the corresponding operator or function (for operator requirements with non-void return types) or a single expression statement containing a use of the corresponding operator or function (for operator requirements with a void return type). Implicitly-dened functions for signatures do not have linkage and cannot have their addresses taken. 2. The implicitly generated denition for a free function requirement contains an unqualied call to a function of the same name. [Example:
29
end example] 3. The implicitly generated denition for an operator requirement contains a use of the corresponding operator.1 [Example:
concept C<typename T> { bool operator<(T, T); T operator-(T); } concept map C<int> { bool operator<(int x, int y) { return x < y; } T operator-(T x) { return -x; } };
end example] 4. If the implicitly generated denition of a signature fails to type-check, and the signature contains a default implementation, the default implementation is used. [Example:
concept EqualityComparable<typename T> { bool operator==(T, T); bool operator!=(T x, T y) { return !(x == y); } } class X {}; bool operator==(X, X); concept map EqualityComparable<X> { bool operator==(X x1, X x2) { return x1 == x2; } // implicitly generated bool operator==(X x1, X x2) { return !(x1 == x2); } // from default };
end example] 5. Implicitly-dened associated type denitions shall be provided when the associated type requirement contains a default type expression. The implicitly-dened denition substitutes the concept arguments into the default type expression. [Example:
Note that we do not treat operators like free functions so that operators can match built-in operators or operators dened as members.
1
30
end example] 6. Implicitly-dened associated type denitions shall be provided when an associated type without a default type expression is used as the return value of a signature. The associated types dened value shall be the return type of the corresponding function in the concept map, which may be implicitly or explicitly dened. [Example:
concept UnaryFunction<typename F, typename T> { typename result type; result type operator()(F&, T); }; stuct identity {}; concept map UnaryFunction<identity, int> { int operator()(identity&, int x) { return x; } // implicitly generated: typedef int result type; }; concept map UnaryFunction<oat ()(oat), oat> { // implicitly generated: oat operator()(oat (&f)(oat), oat x); // implicitly generated: typedef oat result type; }; template<CopyConstructible T> concept map UnaryFunction<T ()(T), T> { // implicitly generated: T operator()(T (&f)(T), T x); // implicitly generated: typedef T result type; };
end example] 7. Implicitly-dened associated value and template denitions shall be provided when the associated value or template requirement contains a default value. The implicitlydened denition substitutes the concept arguments into the default value. 3.3.4 Renements and Concept Maps
When a concept map is dened for a concept C that has a renement clause, concept maps for each of the renements of C are implicitly dened from the denition of the concept map for C .2 [Example:
It is not necessary that these concept maps be implicitly generated, so long as they can be found and synthesized during concept map matching.
2
31
end example] 1. Concept map templates will be implicitly dened for renements only when all of the template parameters of the original concept map are deducible from the renement. [Example:
concept Ring<typename AddOp, typename MulOp, typename T> : Group<AddOp, T>, Monoid<MulOp, T> { / ... / }; template<Integral T> concept map Ring<std::plus<T>, std::multiplies<T>, T> { } // okay, implicitly generates: template<Integral T> concept map Group<std::plus<T>, T> { } template<Integral T> concept map Monoid<std::multiplies<T>, T> { } template<Integral T, Integral U, Integral V> where MutuallyConvertible<T, U> && MutuallyConvertible<T, V> && where MutuallyConvertible<U, V> concept map Ring<std::plus<T>, std::multiplies<U>, V> { } // above concept map is ill-formed, cannot implicitly generate: template<Integral T, Integral U, Integral V> where MutuallyConvertible<T, U> && MutuallyConvertible<T, V> && where MutuallyConvertible<U, V> concept map Group<std::plus<T>, V> { } template<Integral T, Integral U, Integral V> where MutuallyConvertible<T, U> && MutuallyConvertible<T, V> && where MutuallyConvertible<U, V> concept map Monoid<std::multiplies<U>, V> { }
end example] For renements that are not implicitly dened: Concept maps matching the rened concept map shall have been previously dened. [Example:
concept Ring<typename AddOp, typename MulOp, typename T> : Group<AddOp, T>, Monoid<MulOp, T> { / ... / }; template<Integral T, Integral V> where MutuallyConvertible<T, V> concept map Group<std::plus<T>, V> { } template<Integral U, Integral V> where MutuallyConvertible<U, V> concept map Monoid<std::multiplies<U>, V> { } template<Integral T, Integral U, Integral V> where MutuallyConvertible<T, U> && MutuallyConvertible<T, V> && where MutuallyConvertible<U, V>
32
end example] Concept maps for which implicit concept maps cannot be generated from renements shall not dene functions ( 3.3.1) for signatures in those rened concepts. 2. If a concept map has been dened explicitly, it will not be dened implicitly due to renement. [Example:
concept ForwardIterator<typename Iter> { / ... / } concept BidirectionalIterator<typename Iter> : ForwardIterator<Iter> { / ... / } concept map ForwardIterator<list iter> { / ... / } concept map BidirectionalIterator<list iter> { / ... / } // does not implicitly generate concept map ForwardIterator<list iter>
When a concept map is required for a given concept-id, concept map matching selects the most specialized concept map. Concept map matching is required when using a constrained template (to satisfy a concept-id requirement), when verifying that a concept map meets the nested requirements of its corresponding concept, or when performing qualied name lookup into a concept map. 1. concept map matching determines which concept map templates match the required concept-id by matching the template arguments to the concept with the template arguments to the concept map templates. concept map matching selects the most specialized concept map using the same partial ordering rules as for class template partial specializations, extended by partial ordering with where clauses ( 3.4.6). If partial ordering of concept maps results in an error or ambiguity, the program is illformed. 2. If no matching concept map is found and the corresponding concept is an explicit concept, concept map matching fails. 3. If no matching concept map is found and the corresponding concept is an implicit concept, the compiler tentatively generates an empty concept map denition for the concept-id. The concept map will be type-checked with all of its denitions implicitly generated ( 3.3.3). If type-checking succeeds, the concept map denition is generated and concept map matching succeeds. If type-checking fails, the concept map denition is removed along with any other failed instantiations and concept map matching fails. 4. A well-formed program may include failures in concept map matching.
33
3.4
Constrained Templates
template-declaration : exportopt template < template-parameter-list > where-clause declaration member-declaration : where-clause member-declaration Constrained templates are templates that have constraints placed on their template parameters. Unlike existing (unconstrained) templates, constrained templates are completely type-checked at the time of their denition. A constrained template veries that its template arguments meet its stated constraints prior to instantiation. Thus, a well-formed constrained template will only fail to instantiate under very rare circumstances, providing nearly-perfect separate type checking. 1. A template is constrained if any requirements have been placed on its template parameters, either through the presence of a where clause ( 3.4.1) or inline requirements ( 3.4.2). 2. A template that is not constrained is unconstrained. 3.4.1 Where Clauses
where-clause : where constraint-expr constraint-expr : constraint-expr || and-constraint and-constraint and-constraint : and-constraint && not-constraint not-constraint not-constraint : ! constraint constraint constraint : ::opt nested-name-specier opt concept-id ( constraint-expr ) where clauses place constraints (requirements) on the parameters of a template. The constraints described in where clauses have two roles. First, they restrict the use of the constrained template to template arguments that satisfy the requirements of the where clause. Second, they provide declarations available within the denition of the template that will be used for type-checking.
34
1. A concept-id constraint in a where clause requires the existence of a concept map. When type-checking a constrained template, the concept-id constraint provides declarations from its associated concept for type-checking the denition of the constrained template. A concept-id constraint is satised when concept map matching (3.3.5) nds a unique concept map. [Example:
concept Addable<typename X> { X operator+(X, X); } concept map Addable<int> { }; template<typename T> where Addable<T> T plus(T x, T y) { return x + y; // okay: Addable<T> provides T operator+(T, T); } int x = 17, y = 42; plus(x, y); // okay: Addable<int> requirement is satised by concept map Addable<int>
end example] 2. A not constraint !C<T1, T2, ..., TN> requires that no concept map exist for the specied concept, i.e., concept map matching ( 3.3.5) must fail for the given concept-id. When type-checking a constrained template, not constraints do not introduce any declarations into the template denition. Their only impact on type-checking is the propagation of the fact that no concept map exists for that concept. [Example:
concept C<typename T> { / ... / } template<Regular T> where !C<T> void f(T x) { f(x); // okay, no concept map for C<T> } concept map C<int> { / ... / }; oat x; int y; f(x); // okay, no concept map C<oat> f(y); // error: there exists a concept map C<int>
end example] 3. An and constraint C1 && C2 requires that both of the constraints C1 and C2 be satised. When type-checking a constrained template containing an and constraint, the union of C1 and C2 is available to the template denition. [Example:
concept EqualityComparable<typename T> { bool operator==(T, T); } concept LessThanComparable<typename T> { bool operator<(T, T); } template<Regular T> where EqualityComparable<T> && LessThanComparable<T>
35
end example] 4. An or constraint C1 || C2 requires that either the constraint C1 must be satised or the constraint C2 must be satised, but not both. When type-checking a constrained template containing an or constraint, the intersection of C1 and C2 is available to the template denition. [Example: The following function cos() accepts either integral or oating-point types.
concept Integral<typename T> { / ... / } concept Floating<typename T> { / ... / } template<Regular T> where Integral<T> || Floating<T> T cos(T); class anomaly { / ... / }; concept map Integral<anomaly> { / ... / }; concept map Floating<anomaly> { / ... / }; cos(anomaly()); // error: have both Integral<anomaly> and Floating<anomaly>
end example] 5. The canonical representation for compound where clauses is disjunctive normal form. Two where clauses are considered equivalent if their disjunctive normal forms are equivalent. [Example:
concept Integral<typename T> { / ... / } concept Floating<typename T> { / ... / } template<typename T> where Regular<T> && (Integral<T> || Floating<T>) T cos(T); template<typename T> where (Regular<T> && Integral<T> || Regular<T> && Floating<T>) T cos(T x) { / denes function above. / } template<typename T> where Regular<T> && !(!Integral<T> && !Regular<T>) T cos(T x) { / error: redenition of cos / }
end example] 6. A constrained template with an or constraint is equivalent to a set of specialized constrained templates, each of which contains one term from the disjunctive normal form of the where clause. [Example:
concept Integral<typename T> { / ... / } concept Floating<typename T> { / ... / } template<Regular T> where Integral<T> || Floating<T> T cos(T) { / body text / } // equivalent to... template<Regular T> where Integral<T> T cos(T) { / same body text / } template<Regular T> where Floating<T> T cos(T) { / same body text / }
36
7. When a constrained template contains an or constraint C1 || C2, the meaning of identiers shall not dier between C1 and C2.3 [Example:
concept C1<typename T> { typename inverse; where Convertible<T, inverse>; } concept C2<typename T> { T inverse(T); } template<typename T> where C1<T> || C2<T> // error: inverse has dierent meanings in C1 and C2 f(T t) { inverse(t); }
concept-name : identier type-parameter : ::opt nested-name-specier opt ::opt nested-name-specier opt ::opt nested-name-specier opt ::opt nested-name-specier opt concept-name identier concept-name identier = type-id concept-id identier concept-id identier = type-id
Inline requirements oer an alternative way of specifying the constraints on template parameters. Inline requirements support the type of a type view of concepts, and are therefore limited to concepts whose rst parameter is a template type parameter. 1. With the exception of nested name lookup, an inline requirement C<T1, T2, ..., TN> T or C T stated in a template header is equivalent to a constrained template that declares T as typename T in its template header and introduces the concept-id constraint C<T, T1, T2, ..., TN> into the where clause. [Example:
concept C<typename T> { / ... / } template<C T> void f(T); // is equivalent to the following, modulo nested type lookup template<typename T> where C<T> void f(T);
This restriction is intended to ensure that an implementation need only parse a constrained template once, rather than producing several dierent abstract syntax trees corresponding to each term in the disjunctive normal form.
3
37
2. Given an inline requirement C T or C<T1, T2, ..., TN> T for a constrained template, the members of concept C and its renements are available through qualied lookup into T.4 [Example:
concept InputIterator<typename T> { typename dierence type; / ... / } template<InputIterator Iter> void advance(Iter& x, Iter::dierence type n); // Iter::dierence type is the same as InputIterator<Iter>::dierence type
end example] 3. When inline requirements are used with multi-type parameters, associated types are only injected into the template type parameter being declared. [Example:
concept UnaryFunction<typename F, typename T> { typename result type; result type operator()(F&, T); }; template<typename T, UnaryFunction<T> F> F::result type apply(F& f, const T& t) { return f(t); } // F::result type is the same as UnaryFunction<F, T>::result type
end example] 4. Multi-type parameters may be used with the form C T if all the concept parameters of C beyond the rst have default values. [Example:
concept EqualityComparable<typename T, typename U = T> { bool operator==(T, U); }; template<EqualityComparable T> bool equal(const T& x, const T& y) { return x == y; } // okay
end example] [Alternative: Instead of making concept Cs members available in the scope of T when given the inline requirement C T, we could make the members of every concept-id C<T1, T2, ..., TN> that has T in its argument list (e.g., C<T>, C<U> T, C<T, U>, but not C<U, V>) available in T:
concept InputIterator<typename Iter> { typename value type; } template<typename Iter> where InputIterator<Iter> && CopyConstructible<Iter::value type> Iter::value type deref(Iter f);
4
Note that this syntactic shortcut does not mandate that T actually have these members nested.
38
In this case, given Iter::value type the compiler will search for value type in InputIterator<Iter> (since Iter is an argument) but not CopyConstructible<Iter::value type>. Thus, it will nd InputIterator<Iter>::value type. In contrast, the current qualied name lookup rules for constrained template parameters will make the above example ill-formed. Both forms work equally well when Iter is declared using an inline requirement:
template<InputIterator Iter> where CopyConstructible<Iter::value type> Iter::value type deref(Iter f);
This alternative has the advantage that inline requirements are equivalent to where clauses, both for name lookup and semantically. It also treats all parameters to concepts equally, because one can nd the members of a concept-id C<T1, T2, ..., TN> inside any of the parameters T1, T2, ..., TN. On the other hand, by making the names of members from several concepts available to all of their template arguments, we increase the risk of an ambiguity:
concept Assignable<typename T, typename U = T> { typename result type; result type operator()(T&, U); }; concept BinaryOperation<typename BinOp, typename T, typename U> { typename result type; result type operator()(BinOp&, T, U); } template<typename T, typename U, BinaryOperation<T, U> BinOp> where Assignable<BinOp> BinOp::result type f(BinOp op, T, U); // error: BinOp::result type
In this code, BinOp::result type is ambiguous when using the alternative qualied name lookup semantics because the compiler searches in both the inline requirement BinaryOperation<T, U> BinOp and the requirement Assignable<BinOp>, both of which have a result type. With the current qualied name lookup semantics, we only search inside the inline requirement. Note also that, because all arguments are treated equally, T::result type will nd BinaryOperation<BinOp, T, U>::result type. end alternative] 3.4.3 Type-Checking Constrained Templates
Constrained templates provide complete type-checking at the time of denition, which we refer to as implementation-side type checking. Combined with client-side type-checking against the requirements in the where clause, constrained templates provide nearly separate type checking: if a given set of template arguments meets the requirements in the where clause of a given constrained template, the corresponding instantiation is guaranteed to succeed unless there exist inconsistent specializations or partial ordering ambiguities. [Example:
template<InputIterator Iter, typename F> where Callable1<F, reference> F for each(Iter rst, Iter last, F f) { while (rst < last) { // error: no < operator dened
39
end example] 1. Type-checking of a constrained template occurs as if each template type parameter, template-id whose template-name is a template template parameter, and associated type has been replaced by an archetype. 5 Archetypes are unique class types that provide only the member functions that occur as requirements in the constrained template ( 3.4.1, 3.4.2). None of the implicitly dened class members (default constructor, copy constructor, destructor, assignment operator) are provided for archetypes. 2. Types made equivalent by same-type constraints ( 3.5.1) share the same archetype. [Example:
concept CopyConstructible<typename T> { T::T(const T&); T::T(); }; template<CopyConstructible T, typename U> where SameType<T, U> void f(const T& t) { T copy(t); // okay: T::T(const T&) from CopyConstructible U other copy(t); // okay: U has the same archetype as T };
end example] 3. Dependent names in the body of a constrained template are looked up in the concepts associated with each concept-id requirement (including renements of the associated concept and nested requirements) stated in the where clause and as nested requirements. For the determination of dependent names, an expression is type-dependent if it would have been type-dependent in an unconstrained template. [Example:
concept SignedIntegral<typename T> { T::T(int); T& operator++(T&); }; concept EqualityComparable<typename T> { bool operator==(T, T); bool operator!=(T, T); };
With all dependent types in a constrained template being replaced with non-dependent types, constrained templates type-check like non-templates.
5
40
concept InputIterator<typename Iter> : EqualityComparable<Iter> { typename dierence type; where SignedIntegral<dierence type>; typename value type; Iter& operator++(Iter&); value type operator(Iter); }; template<InputIterator Iter> where EqualityComparable<Iter::value type> Iter::dierence type count(Iter rst, Iter last, const Iter::value type& value) { Iter::dierence type result = 0; while (rst != last) { // okay, EqualityComparable<Iter>::operator!= if (rst == value) // okay, EqualityComparable<value type>::operator== ++result; // okay, SignedIntegral<dierence type>::operator++ ++rst; // okay, InputIterator<Iter>::operator++ } return result; // okay, CopyConstructible<dierence type> constructor6 }
end example] 4. Dependent names not found within the concepts will be looked up in the lexical scope. [Example:
template<InputIterator InIter, OutputIterator<InIter::value type> OutIter> OutIter copy(InIter rst, InIter last, OutIter out); // #1 template<MutableForwardIterator InIter, OutputIterator<InIter::value type> OutIter> OutIter rotate copy(InIter rst, InIter middle, InIter last, OutIter result) { return copy(rst, middle, copy(middle, last, result)); // okay: copy refers to #1 }
end example] 5. In a constrained template, overload resolution and the selection of the most-specialized template for a given set of template arguments are both performed based on the requirements of the constrained template.7 [Example:
concept InputIterator<typename Iter> { }; concept BidirectionalIterator<typename Iter> : InputIterator<Iter> { }; concept RandomAccessIterator<typename Iter> : BidirectionalIterator<Iter> { }; template<InputIterator Iter> void advance(Iter&, Iter::dierence type); // #1 template<BidirectionalIterator Iter> void advance(Iter&, Iter::dierence type); // #2
6 7
This requirement is automatically generated via constraint propagation. Note that #3 will again be considered at instantiation time. See Section 3.4.4.
41
end example] [Alternative: Name lookup within a constrained template only searches the where clause for dependent names. This rule is intended to match the semantics of existing templates closely, because non-dependent names retain their current lookup semantics (ignoring the where clause). For dependent names, name lookup searches the where clause and, if the name is found, this name hides other declarations with the same name from outer scopes. There are alternative name lookup rules we could imply. The most interesting of these treats name lookup into the where clause as a set of using directives that pull the signature names from the concepts into the scope of the constrained template. This approach diers from the dependent-name approach in two ways. First, it eliminates the distinction between dependent and non-dependent names entirely, so that all unqualied name lookups consider names in the where clause. Second, names in the where clause will overload names found in the lexical scope, rather than hiding them. end alternative] 3.4.4 Instantiating Constrained Templates
Constrained template instantiation is very similar to instantiation of unconstrained templates. During constrained template instantiation, template parameters are replaced by their corresponding template arguments throughout the template, therefore producing a concrete implementation (function, class, or concept map). However, constrained template instantiation limits the amount of name lookup that occurs relative to unconstrained template instantiation, e.g., argument-dependent lookup is not used when instantiating constrained templates. 1. Instantiation of constrained templates replaces references to signatures and associated types inside a concept with the actual function and type denitions provided by the concept map. The eect is as if the constrained template were written with only qualied uses of names within concepts:8 [Example:
template<InputIterator Iter, typename F> where Callable1<F, reference> F for each(Iter rst, Iter last, F f) { while (InputIterator<Iter>::operator!=(rst, last)) { typedef typename InputIterator<Iter>::reference reference; // exposition only Callable1<F, reference>::operator()(f, InputIterator<Iter>::operator(rst)); InputIterator<Iter>::operator++(rst); } return f; }
This translation ensures that the syntax adaptation provided by concepts maps is employed during instantiation.
8
42
2. Instantiation of function calls for dependent names not matched in the where clause undergo a second stage of partial ordering to select the most specialized function from a set of candidate functions. Given the seed function that was used for type-checking the constrained template ( 3.4.3), the set of candidate functions includes the seed and all functions in the same scope as the seed that have the same function signature (template parameters, function parameters, return type) as the seed and are more specialized than the seed. [Example:
template<InputIterator Iter> void advance(Iter& i, Iter::dierence type n); // A template<BidirectionalIterator Iter> void advance(Iter& i, Iter::dierence type n); // B template<RandomAccessIterator Iter> void advance(Iter& i, Iter::dierence type n); // C template<BidirectionalIterator Iter> void foo(Iter i) { advance(i, 1); // type-checks against B; candidate set is {B, C} }
It is often the case that certain requirements on template parameters are apparent from the declaration of a constrained template, even if they are not explicitly stated. These requirements (constraints) are implicitly added to the requirements of the template through the process of constraint propagation. 1. For every type T that appears as an argument or return type in a function declarator, the requirement std::CopyConstructible<T> is implicitly generated. [Example:
template<EqualityComparable T> bool eq(T x, T y); // implicitly generates requirement CopyConstructible<T>
end example] 2. For every template-id X<A1, A2, ..., AN> that appears in the declaration of a constrained template T, where X is also a constrained template, the requirements of X are implicitly generated in T. [Example:
template<LessThanComparable T> class set { / ... / }; template<CopyConstructible T> void maybe add to set(std::set<T>& s, const T& value); // use of std::set<T> implicitly generates requirement LessThanComparable<T>
end example]
43
Function, class, and concept map templates can be partially ordered based on their function arguments and template arguments, using the rules in 14.5.5.2 and 14.5.4.2 of the C++ standard, respectively. [Example:
concept<typename T> A { }; concept<typename T> B : A<T> { }; concept<typename T> C { }; template<typename T> where A<T> void f(T x) { std::cout << 1; } template<typename T> where B<T> void f(T x) { std::cout << 2; } template<typename T> where A<T> void g(T x) { std::cout << 3; } template<typename T> where A<T> && C<T> void g(T x) { std::cout << 4; } concept map B<int> { }; concept map C<int> { }; int main() { f(17); // outputs 2 g(42); // outputs 4 }
end example] 1. If a constrained template and an unconstrained are identical modulo the where clause and template parameter names, the constrained template is more specialized. 2. If two constrained templates are identical modulo the where clause and template parameter names, the templates shall be ordered based on the requirements in the where clauses using the following algorithm. Let T1 and T2 be the two constrained templates. (a) Introduce the requirements from the where clause of T1 into a new environment. (b) Check each of the requirements in the where clause of T2 to determine if they are satised in the new environment. If so, T1 is at least as specialized as T2 . (c) Repeat the process with a new environment, to determine if T2 is at least as specialized as T1 . (d) If T1 is at least as specialized as T2 , but T2 is not at least as specialized as T1 , then T1 is the more specialized template. Similarly, we can determine if T2 is more specialized than T1 .
44
unary-expression : late_check unary-expression elaborated-type-specier : late_check elaborated-type-specier concept-map-denition : concept_map concept-id late_check { concept-map-member-specicationopt } ;opt block-declaration : where-clause ; Concepts provide the ability to separately type-check constrained template denitions from their uses. Concepts are expressive enough to express manybut not alluses of C++ templates. Thus, some template code that relies on certain template tricks will not be expressible inside constrained templates. The late check keyword provides an escape hatch for constrained templates. A latechecked expression, type, or concept map, marked with the late check qualier, is parsed as if that expression, type, or concept map was in an unconstrained template. Within the late-checked expression, type, or concept map, all template type parameters are treated as dependent types and no type-checking is performed. 1. The presence of a where clause within a block introduces requirements into the block that will not be veried until instantiation time. 2. Late-checked expressions are not type-checked until instantiation time. [Example:
template<CopyConstructible T> T unsafe add(T x, T y) { auto result = late check(x+y); // okay, type of result varies where Convertible<decltype(result), T>; // assume result convertible to T return result; // okay, Convertible<decltype(result), T>::operator T }
end example] 3. Late-checked types are not type-checked until instantiation time. [Example:
template<CopyConstructible T> late check typename metafunc<T>::result result type; tricky meta(T x) { typedef late check typename metafunc<T>::result result type; where Convertible<T, result type>; return x; }
end example]
45
4. Late-checked concept maps are not type-checked or veried for consistency with their associated concept until instantiation time. [Example:
template<IteratorTraits Iter> where Convertible<Iter::iterator category, forward iterator tag> concept map ForwardIterator<Iter> late check { ... };
end example] Note that this escape hatch, using late-checked expressions, types, and concept maps, is in ux. We are still attempting to determine what the right level of granularity for such a feature is (e.g., is expression-level too ne-grained?) and are gathering interesting examples.
3.5
Header concepts
The concepts header provides core concepts that can eect the compilation and typechecking process. All concepts reside in namespace std. Note that this description of the <concepts> header is only a summary; the complete description is available in a separate document describing changes to the Standard Library [8]. 3.5.1 Concept SameType
concept SameType<typename T, typename U> { / unspecied / }; template<typename T> concept map SameType<T, T> { / unspecied / };
The SameType concept states that its two type parameters refer to precisely the same type. 1. When a constrained template contains a constraint SameType<T, U>, type-checking the use of the template is as-if SameType was specied as an explicit concept with only a single concept map:
template<typename T> concept map SameType<T, T> { };
[Example:
template<typename T, typename U> where SameType<T, U> void f(T, U); int x; long y; f(x, y); // error: SameType<int, long> does not hold
end example] 2. When a constrained template is dened and type-checked, the presence of a same-type constraint makes the two type arguments identical.9 [Example:
The type-checking behavior of the SameType concept is central to the notion of type equality in a compiler and cannot be emulated in a library.
9
46
end example] 3. A program that contains a concept map SameType<T, U>, where T = U, is ill-formed. 3.5.2 Concept True
The True concept allows the use of integral constant expressions in where clauses.10 [Example:
template<Regular T> where True<sizeof(T) <= 128> void f(T);
The CopyConstructible concept applies to types that can be passed as arguments to or returned from a function. Although the denition of the CopyConstructible concept can be expressed entirely in a library, CopyConstructible is a core concept because CopyConstructible constraints are generated from function signatures via constraint propagation (3.4.5).
3.6
Miscellaneous
This section describes the eects that concepts will have on other parts of the C++ language, motivating each change with examples.
Although the True concept can be dened entirely within a library, it is provided as a core concept to allow implementations to provide improved diagnostics.
10
47
When certain special class members are not declared explicitly within a class, these class membersdefault constructors, destructors, copy constructors, and assignment operators are implicitly declared and, when necessary, dened. These implicit declarations open up a loophole in the concept system, because the declarations will be implicitly generated even if the denitions will not compile properly. For instance, if a class type X has a private default constructor, DefaultConstructible<X> will not hold; DefaultConstructible<std::pair<X, X> >, however, will hold, because the pair default constructor is implicitly declared, even though its denition will fail to compile. Users can avoid these problems using where clauses on explicitly-dened constructors, destructors, and assignment operators. For instance:
template<typename T, typename U> struct pair { where DefaultConstructible<T> && DefaultConstructible<U> pair() : rst(), second() { } where CopyConstructible<T> && CopyConstructible<U> pair(const T& t, const U& u) : rst(t), second(u) { } where Destructible<T> && Destructible<U> pair() { } where Assignable<T> && Assignable<U> pair& operator=(const pair<T, U>& other) { rst = other.rst; second = other.second; return this; } T rst; U second; };
Unfortunately, this change would require modications to much existing code. Instead, we opt to only declare implicit default constructors (12.1p5), destructors (12.4p3), copy constructors (12.8p4), and copy assignment operators (12.8p10) when the denitions will type-check properly, eliminating the problem without requiring changes to existing code. 3.6.2 Default Arguments
Default function and template arguments will only be type-checked when they are used. For instance, consider the following constructor for the vector class template:
template<CopyConstructible T> class vector { public: vector(size t n, const T& value = T()); };
48
If the default argument were type-checked at denition time, the code would be ill-formed: T is not required to have a default constructor. However, this constructor can be used with types that dont have default constructors. One could split this constructor into two separate constructors:
template<CopyConstructible T> class vector { public: vector(size t n, const T& value); where DefaultConstructible<T> vector(size t n); };
Unfortunately, requiring this transformation for all templates with default argument would make default parameters essentially useless in concept-based template libraries. Instead, we state that default arguments are not type-checked until they are required. We still catch errors at the right time, when the caller attempts to make use of the default argument:
template<CopyConstructible T> vector<T> make n vec(size t n) { return vector<T>(n); // error: T has no default constructor }
In essence, this rule is just an extension of the existing rule for default arguments in templates, which states that they will not be instantiated unless required by the caller.
Revision History
Since N2042=06-0112: Added support for rvalue references. Fixed numerous typos and grammatical errors.
Acknowledgments
The eort to introduce concepts into C++ has been shaped by many. The authors of the Indiana and Texas concepts proposals have had the most direct impact on concepts: Gabriel Dos Reis, Ronald Garcia, Jaakko J arvi, Andrew Lumsdaine, Jeremy Siek, and Jeremiah Willcock. Other major contributors to the introduction of concepts in C++ include David Abrahams, Matthew Austern, Mat Marcus, David Musser, Sean Parent, Sibylle Schupp, and Alexander Stepanov. Howard Hinnant helped introduce support for rvalue references. This work was supported by NSF grant EIA-0131354 and by a grant from the Lilly Endowment.
References
[1] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference Manual. Addison-Wesley, Reading, MA., 1990.
Doc. no: N2081=06-0151 [2] Douglas Gregor. ConceptGCC: Concept extensions for C++. generic-programming.org/software/ConceptGCC, 2006.
49 http://www.
[3] Douglas Gregor, Jaakko J arvi, Jeremy Siek, Bjarne Stroustrup, Gabriel Dos Reis, and Andrew Lumsdaine. Concepts: Linguistic support for generic programming in C++. In Proceedings of the 2006 ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA 06), October 2006. Accepted. [4] Douglas Gregor and Jeremy Siek. Explicit model denitions are necessary. Technical Report N1798=05-0058, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, May 2005. [5] Douglas Gregor and Jeremy Siek. Implementing concepts. Technical Report N1848=050108, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, August 2005. [6] Douglas Gregor, Jeremy Siek, Jeremiah Willcock, Jaakko J arvi, Ronald Garcia, and Andrew Lumsdaine. Concepts for C++0x (revision 1). Technical Report N1849=050109, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, August 2005. [7] Douglas Gregor and Bjarne Stroustrup. Concepts. Technical Report N2042=06-0112, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, June 2006. [8] Douglas Gregor, Jeremiah Willcock, and Andrew Lumsdaine. Concepts for the C++0x Standard Library: Utilities (revision 1). Technical Report N2082=06-0152, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, October 2006. [9] Bjarne Stroustrup. Concepts a more abstract complement to type checking. Technical Report N1510=03-0093, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, October 2003. http://www.open-std.org/jtc1/ sc22/wg21. [10] Bjarne Stroustrup and Gabriel Dos Reis. Concepts design choices for template argument checking. Technical Report N1522=03-0105, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, October 2003. http://www.open-std.org/jtc1/sc22/wg21. [11] Bjarne Stroustrup and Gabriel Dos Reis. Concepts syntax and composition. Technical Report N1536=03-0119, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, October 2003. http://www.open-std.org/jtc1/ sc22/wg21. [12] Bjarne Stroustrup and Gabriel Dos Reis. A concept design (rev. 1). Technical Report N1782=05-0042, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, May 2005.