January 31, 2017·5 min read·engineeringcppstandardstemplatesconcepts

Stroustrup argues for concepts as template interfaces

The January concepts paper argues that generic programming needs explicit requirements without runtime dispatch. Its design questions concern semantic meaning as much as shorter template syntax.

An ordinary function declaration tells a caller which argument types it accepts. An unconstrained template can defer much of that information to errors deep inside its definition. Bjarne Stroustrup's January 31 paper, Concepts and generic programming, argues that templates need explicit interfaces while preserving their generality and runtime efficiency. Concepts name requirements on template arguments so that a caller can fail against the declared requirements instead of an incidental expression in an implementation.

The paper describes the Concepts Technical Specification and an implementation already available in GCC. The GCC 6 changes document the -fconcepts option for that support. This is work on a technical specification and its use, not an announcement that concepts have become part of the main C++ standard.

I value templates when abstraction disappears from the runtime work, but their diagnostics are a substantial cost. A declaration that states what an algorithm requires can make that cost more manageable. The strongest part of Stroustrup's argument is the connection between a useful interface and the meaning of the algorithm, beyond finding a shorter way to write template syntax.

Requirements are compile-time predicates

Stroustrup describes a concept as a predicate evaluated during compilation. A constraint can require operations on a type, or a relationship among several types. It does not require objects to inherit a common base class or use virtual calls for those operations.

This matters for an algorithm such as finding a value in a sequence. The sequence must supply iteration, but the sought value also needs a meaningful comparison with the sequence's element type. Checking only that the container has an equality operation on itself would miss that relationship. A multi-argument concept can describe the comparison that the algorithm actually uses.

The paper's syntax belongs to the Concepts TS design under discussion. This small example follows its concept bool form and asks whether equality and inequality results convert to bool.

template<typename T>
concept bool Equality_comparable =
    requires(T a, T b) {
        { a == b } -> bool;
        { a != b } -> bool;
    };

template<typename T>
    requires Equality_comparable<T>
bool same_value(const T& left, const T& right)
{
    return left == right;
}

The expressions inside the requirement are not executed as a runtime test. They describe uses that the compiler checks when deciding whether a type satisfies the predicate. The function body still performs an ordinary equality comparison when the selected specialization executes.

The distinction avoids a common misunderstanding about the cost model. A constraint helps select a valid template and reject invalid arguments. It does not add a runtime table of permitted types or require every call to dispatch through an interface object. Stroustrup explicitly presents zero additional runtime cost relative to unconstrained template code as part of the design.

A meaningful concept needs more than available syntax

The paper emphasizes semantic coherence. A collection of operations can be syntactically available without forming the abstraction an algorithm needs. An equality expression that returns something convertible to bool does not, by that fact alone, establish all the mathematical properties a programmer associates with equality.

That leaves a role for library design and documented semantic requirements. A concept should identify a useful domain abstraction, not merely collect expressions that happen to occur in one function body. Otherwise each implementation detail can become a distinct requirement name, leaving users with many predicates and little guidance about how their types relate to algorithms.

Stroustrup's sequence and sortable examples build requirements from other concepts. A sortable sequence needs the relevant iteration capability as well as a suitable comparison on element values. Naming those requirements makes an algorithm's needs readable, while their composition avoids restating every low-level operation in each declaration.

The compiler can check the expressible syntactic portion of that account. The reader still needs to understand the semantics that make an operation appropriate. Concepts improve the place where requirements are stated, but they do not convert arbitrary program behavior into a theorem automatically checked during template selection.

Existing types can satisfy requirements without registration

The design does not require an explicit declaration that a type models a concept. If its properties satisfy the constraint, it matches. This allows an independently written type and an independently written generic algorithm to work together without either author editing a shared inheritance hierarchy.

Stroustrup also describes constrained overload selection. More specific requirements can select an algorithm that uses additional operations, while a broader overload remains available for other suitable arguments. That mechanism lets a library associate different implementations with capabilities expressed in the declarations.

The relationship among constraints matters here. A library author cannot treat concept names as labels with no internal meaning and still expect useful selection among them. The paper connects the definition of requirements with the compiler's ability to establish which constrained alternative applies.

Its support for shorter notation follows the same interface goal. A single-type concept can appear where a template parameter's requirement belongs, while an explicit requires clause remains useful for relationships involving several parameters. The alternative spellings are a debated design choice, not evidence that every form represents a separate runtime mechanism.

Stroustrup traces the current effort to work with Gabriel Dos Reis and Andrew Sutton beginning in 2009, and a later Palo Alto discussion of a constrained STL. That history explains the emphasis on existing generic algorithms. The design starts with the requirements those algorithms need to express, then asks which language mechanisms can support them without introducing runtime indirection.

Constraints do not finish every template problem

The paper explicitly separates concepts from organizing template definitions. Better constraints do not by themselves remove the need to make definitions available for instantiation. Stroustrup discusses modules as separate work addressing organization, so improved call diagnostics should not be confused with a complete solution to template compilation costs.

I favor judging this design by the quality of the interface a caller reads. A named requirement can explain why an argument is unsuitable before an implementation produces a long chain of failures. It can also expose relationships among types that a bare typename declaration leaves unstated. The remaining obligation is to design those requirements around coherent operations, with semantic promises that library users can understand and types can actually meet.