Document no. P0915R0 Date 2018-02-08 Reply-to Vittorio Romeo <vittorio.romeo@outlook.com>, John Lakos <jlakos@bloomberg.net> Audience Evolution Working Group Project ISO JTC1/SC22/WG21: Programming Language C++

Concept-constrained auto

Abstract

This paper proposes the addition of a concept-constrained auto placeholder type for variables. The primary goal is to increase code readability and correctness without sacrificing genericity:

Table of contents

Overview

A large part of the Concepts TS has been merged into the C++20 Standard working draft . Due to the lack of consensus and also to the existence of reasonable concerns, proposed features such as placeholders (changes to [dcl.spec.auto] ) and abbreviated templates (changes to [dcl.fct] and [temp] ) have not yet been merged into the working draft.

This paper proposes the introduction of a concept-constrained auto type placeholder, focusing on only the simplest, most common, and most impactful use case, while leaving the door open for future extensions.

The proposed concept-constrained auto enables users to specify a (constraining) concept-name when declaring variables using auto :

Motivation

The current unconstrained auto placeholder works well in many scenarios:

Unfortunately, the inability to constrain auto with a concept often results in less-readable code, sometimes with surprising results. Consider the following cases:

We are confident that proper use of concept-constrained auto , where appropriate, will significantly increase the readability and accessibility of Modern C++ programs, especially in large code bases. Note that we are not suggesting that concept-constrained auto should always be preferred to (unconstrained) auto -- there are common situations where supplying the optional constraint would be counter indicated:

In the example above, the identifier it is already clearly some form of Iterator . Moreover, the Iterator concept, along with its idiomatic use as being what is expected of the return types for begin() and end() methods, are familiar to virtually every C++ developer. (We refer to such ubiquitously familiar concepts as vocabulary concepts). Making Iterator an explicit (named) constraint to auto here would be pointless noise – arguably detracting from readability. If, on the other hand, it were the case that (1) the code did not as yet make explicit use of random-access-iterator features, and (2) it was foreseeable that such features would soon be required, then constraining the auto with RandomAccessIterator is exactly what would express that engineering intent:

From an engineering standpoint, however, the use of concept-constrained auto becomes imperative, when dealing with non-vocabulary concepts, as this compiler-enforced “documentation” dramatically facilitates developers becoming familiar with previously unseen concepts by name, which – in turn – can be more easily looked up if need be:

Developers reading the function above will benefit greatly from the explicit bdex::OutStream concept constraint, especially those who find it unfamiliar:

Being able to see bdex::OutStream gives readers a name to search for in the code base and its documentation to understand the scope and functionality of the concept.

The auto<...> notation (as opposed to the “as yet to be adopted” terse notation) makes it unambiguously clear that bdex::OutStream is a concept and not a type.

If (unconstained) auto were used, the reader would need to check the declaration of getLogStream() (and possibly its definition) in order to understand what kind of object was being returned.

Proposed syntax

We propose the ability of constraining auto with a concept, independently of cv-qualifiers. E.g.

Wording

The proposed wording copies the terminology used in N4674 in order to make future extensions easier to apply.

[dcl.type.simple]

Add constrained-type-specifier to the grammar for simple-type-specifiers.

Modify paragraph 2 to begin: The simple-type-specifier auto specifier is a placeholder for a type to be deduced (10.1.7.4). The simple-type-specifier auto and constrained-type-specifiers are placeholders for a type to be deduced (10.1.7.4).

Add constrained-type-specifier to the table of simple-type-specifiers in Table 11: Specifier(s) Type constrained-type-specifier placeholder for type to be deduced

[dcl.spec.auto]

Modify paragraph 1 to begin: The auto and decltype(auto) type-specifiers are used to designate a placeholder type that will be replaced later by deduction from an initializer. The auto and decltype(auto) type-specifiers, and constrained-type-specifier are used to designate a placeholder type that will be replaced later by deduction from an initializer.



[dcl.spec.auto.deduct]

Modify paragraph 3 to begin: If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. If the placeholder is the auto type-specifier or a constrained-type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction.

Modify paragraph 3: Deduce a value for U using the rules of template argument deduction from a function call (17.8.2.1), where P is a function template parameter type and the corresponding argument is e . If the deduction fails, the declaration is ill-formed. Deduce a value for U using the rules of template argument deduction from a function call (17.8.2.1), where P is a function template parameter type and the corresponding argument is e . If the deduction fails, the declaration is ill-formed. If the used placeholder is a constrained-type-specifier with an associated constraint C , the declaration is ill-formed if C<U> evaluates to false .



[dcl.spec.auto.constr]

Add this section to 10.1.7.4.

Paragraph 1: A constrained-type-specifier designates a placeholder type and introduces an associated constraint. constrained-type-specifier: auto < qualified-concept-name > [Example: template<typename T> concept bool C0 = true; int f0() { return 42; } void f1() { auto<C0> i = f0(); // auto<C0> designates a placeholder type // with associated constraint C0 } ]



Future directions

This proposal is meant to be as simple as possible but leaves room for future extensions.

The goal of this proposal is to introduce a simplified yet useful minimal subset of the proposed placeholder sections in the Concepts working draft. Concept-constrained auto ’s scope and functionality can then be expanded in the future to reach the power and flexibility of the original design.

Bikeshedding

<...> is, in our view, the optimal choice due to its clear, concise, and expressive syntax. Unfortunately, it might not be appropriate as it misleadingly suggests template instantiation. Andrew Sutton recommended using the auto|Concept syntax instead, which was part of the original proposal for concepts but was dropped in favor of the terse notation which we have already addressed. There exist other viable possibilities for the concept-constrained auto syntax:

auto{Concept}

auto:Concept

auto Concept

We have not considered auto(Concept) and auto[Concept] because they respectively conflict with variable initialization and structured bindings.

References