Clang 10 brings concepts into its C++2a mode
Clang 10 implements concepts under -std=c++2a. Its experimental caching switch exposes the interaction between constraint semantics and compile-time cost.
Checking whether a type satisfies a requirement can happen repeatedly during template compilation. Concepts give the requirement a language-level representation, but the compiler still has to decide when and how often to evaluate it. The Clang 10 release notes introduce concepts support under -std=c++2a and describe an experimental option to disable constraint-satisfaction caching. The March 24 release makes both the new programming interface and an implementation tension available to users.
The useful story is that a clearer generic interface still has compilation costs. A constraint can appear in several related candidate declarations or instantiations. Reusing an earlier answer can save work, while the language's rules determine when an earlier answer is valid.
Templates can provide the low runtime overhead I value in C++, but the cost often appears during compilation and in diagnostics. Concepts are interesting to me because they let the declaration explain more of the requirements that previously appeared only through failures inside a template body.
State the operation the template needs
A generic function can require that its argument support an expression. Without a visible constraint, the caller may learn about that requirement only when the compiler instantiates the implementation. The resulting diagnostic can involve helper templates that are incidental to the public interface.
A concept lets the author name a requirement and apply it to a template. Andrew Sutton's P0734R0 wording paper describes requires-expressions and the role of constraints in determining whether functions may be used.
This illustrative example checks a simple expression. It uses the syntax supported by the C++20 concepts direction, with Clang 10's release-specific language flag shown separately.
template<class T>
concept HasSize = requires(const T& value) {
value.size();
};
template<HasSize T>
auto element_count(const T& value)
{
return value.size();
}
clang++ -std=c++2a -c example.cpp
The requirement establishes that the expression is well formed. It does not establish that its result is an element count with the semantics an application expects. A library that needs a particular result type or a more specific contract has to state those requirements too.
This is an important limit on attractive names. HasSize is a name chosen by the example's author, not a proof about every type with a member of that spelling. The compiler checks the requirement the source actually gives it.
Constraints participate before the body fails
The benefit of putting a requirement on a declaration is that it becomes part of deciding whether that declaration applies. A caller with an unsuitable type can be rejected through the public requirement. That is a different explanation from an error in an implementation expression reached after selecting an unconstrained template.
For an overload set, this distinction affects which candidates remain relevant. A library can offer operations for different kinds of arguments, with constraints describing the intended boundary. The implementation body then follows the assumptions established at that boundary.
The design still requires care. If two constraints overlap in a way the author did not intend, the caller can face ambiguity. If a requirement omits an operation that the body uses, instantiation can still fail later. Moving requirements to the interface helps only when they describe the implementation accurately.
A diagnostic at that boundary also gives the caller a more relevant vocabulary. It can identify the required expression and the type that failed to supply it. The caller can then decide whether the argument is wrong or whether the interface excludes a useful case. That decision is harder when the first visible failure names a private helper inside the implementation.
That makes constraint design part of ordinary interface design. A public concept should explain why its accepted types are suitable for the promised operation. Merely collecting expressions until a compilation succeeds produces a syntactic boundary whose purpose remains difficult to understand.
Reusing a satisfaction result has semantic consequences
Clang 10 offers -fno-concept-satisfaction-caching for experimentation. The release notes say the draft does not currently permit the caching and warn that disabling it may impose significant compile-time costs. This is a documented implementation issue, not an application feature.
The general tension is easy to understand. If the same question arises several times and its answer remains valid, reusing the answer avoids repeated work. If the language requires a fresh evaluation in circumstances where the relevant information differs, a cached answer needs a justification that simple textual equality cannot supply.
A compiler option exposes the distinction between the desired programming model and the implementation's practical strategy. The user's concept declaration describes a requirement. The compiler's cache is an optimization of how it answers that requirement, so it has to preserve the required result.
The flag can therefore support investigation of a concrete discrepancy. A program whose compilation changes under the flag presents evidence about that implementation choice. It does not establish that every concepts-related error comes from caching or that disabling a cache is an appropriate permanent policy for every build.
I prefer that explicit experimental account to a broad claim that concepts make template compilation cheap. The declaration can become clearer while the implementation still faces repeated work. Both facts matter when a compiler adds a major generic-programming facility.
Language support and library support have distinct requirements
This release implements concepts in the compiler. A program can define its own concept, as the example does, without assuming a particular library supplies a named concept for the same purpose. That keeps the example's requirement visible.
A library facility can depend on additional declarations and algorithms. The fact that the compiler accepts concept syntax does not establish that every library component associated with C++20 is available. An adoption claim needs to identify the compiler and library requirements of the actual source.
For a library author, the practical result is a new way to describe a template's accepted arguments. Clang 10 supplies that language mechanism through its C++2a mode, alongside an option for examining one implementation tradeoff. The useful interface is the one whose constraint states the operations its body needs and whose documented semantics explain what those operations must mean.