**Document Number:** P0658R0

**Date:** 2017-06-11

**Audience:** Evolution Working Group

**Reply-to:** Christopher Di Bella <cjdb.ns@gmail.com>

This document proposes to add *alias-declarations* to *requirement-bodies* so that types may be renamed without the need for creating exposition-only concepts that have additional template typenames for convenience and readability.

Concepts are fun to work with, and fun to design, but anything with more than a few names is tedious to type up, and tedious to read. Consider this rather verbose attempt at drafting a concept for a sequence container:

```
// using value_type_t, difference_type_t, and concepts from Ranges TS
template <typename X, typename... Args>
concept bool SequenceContaioner = Container<X> && requires {
typename value_type_t<X>;
typename difference_type_t<X>;
requires Constructible<X, difference_type_t<X>, value_type_t<X>>();
requires Construictible<X, initializer_list<value_type_t<X>>>();
requires Assignable<X&, initializer_list<value_type_t<X>>>();
// ...
};
```

Our current `SequenceContainer`

is quite jarring to read, so we might like to split it into `SequenceContainer`

and `detail::SequenceConstructible`

:

```
namespace detail {
template <typename X, typename T, typename D, typename... Args>
concept bool SequenceConstructible = Constructible<X, D, T>() &&
Constructible<X, initializer_list<T>>() &&
Assignable<X&, initializer_list<T>>()
// ...
;
// Other SequenceContainer details here...
} // namespace detail
template <typename X, typename... Args>
concept bool SequenceContainer = Container<X> &&
detail::SequenceConstructible<X, value_type_t<X>, difference_type_t<X>, Args...> // &&
// ...
;
```

Now we can read the implementation of a `SequenceContainer`

without the added noise of `value_type_t<X>`

, `difference_type_t<X>`

, and `initializer_list<value_type_t<X>>`

at every turn, but we’ve now created a second, exposition-only concept with exactly one use-case: to facilitate the readability of our usable concept that (hopefully!) has many use-cases. Furthermore, we need to find a meaningful name for our exposition-only concepts so that they are easy to understand.

It would be nice to have something that resembles:

```
template <typename X, typename... Args>
concept bool SequenceContainer = Container<X> && requires {
+ requires using value_type = value_type_t<X>; // type alias value_type, typename requirement for value_type_t<X>
+ requires using difference_type = difference_type_t<X>;
+ using initializer_list_type = initializer_list<value_type>; // just a type alias
requires Constructible<X, difference_type, value_type>();
requires Constructible<X, initializer_list_type>();
requires Assignable<X&, initializer_list_type>();
// ...
};
```

Exposition-only concepts that exist solely to improve readability are removed.

Let’s now consider an attempt to turn `std::inner_product`

into a concept-checked `inner_product`

:

```
template <InputIterator I1, Sentinel<I1> S1, InputIterator I2, Sentinel<I2> S2,
CopyConstructible T,
typename Proj1 = identity, typename Proj2 = identity,
IndirectRegularInvocable<projected<I1, Proj1>,
projected<I2, Proj2>> Op2 = multiplies<>,
RegularInvocable<T,
indirect_result_of_t<Op2&(projected<I1, Proj1>,
projected<I2, Proj2>)>> Op1 = plus<>>
requires
Assignable<T&, const T&>() &&
Assignable<T&, result_of_t<Op1&(T,
indirect_result_of_t<Op2&(projected<I1, Proj1>, projected<I2, Proj2>)>)>>()
T inner_product(I1 first1, S1 last1, I2 first2, S2 last2, T init, Op1 op1 = Op1{},
Op2 op2 = Op2{}, Proj1 proj1 = Proj1{}, Proj2 proj2 = Proj2{});
```

Ugly. There’s lots of repetition, and overall, it’s quite difficult to reason if it’s correct. Let’s tidy that up a bit:

```
template <InputIterator I1, Sentinel<I1> S1, InputIterator I2, Sentinel<I2> S2,
CopyConstructible T,
typename Op1 = plus<>, typename Op2 = multiplies,
typename Proj1 = identity, typename Proj2 = identity,
typename Arg1 = projected<I1, Proj1>, typename Arg2 = projected<I2, Proj2>,
typename InnerResult = indirect_result_of_t<Op2&(Arg1, Arg2)>,
typename OuterResult = result_of_t<Op1&(T, InnerResult)>>
requires
Assignable<T&, const T&>() &&
IndirectRegularInvocable<Op2, Arg1, Arg2>() &&
RegularInvocable<Op1, T, InnerResult>() &&
Assignable<T&, OuterResult>()
T inner_product(I1 first1, S1 last1, I2 first2, S2 last2, T init, Op1 op1 = Op1{},
Op2 op2 = Op2{}, Proj1 proj1 = Proj1{}, Proj2 proj2 = Proj2{});
```

That’s much easier on the eyes, and our operations are properly ordered! Sadly, we’ve introduced an opportunity for Crafty Programmer who thinks that they know better than their compiler (and library specification) to manipulate the template specialisation to allow syntactically-compatible-but-semantically-incompatible types to pass the concept check. This is an unlikely scenario, but we can’t rule it out from happening for all concept-checked functions.

It’d be great if we could do this instead:

```
template <InputIterator I1, Sentinel<I1> S1, InputIterator I2, Sentinel<I2> S2,
CopyConstructible T, typename Op1 = plus<>, typename Op2 = multiplies<>,
typename Proj1 = identity, typename Proj2 = identity>
requires requires {
requires Assignable<T&, const T&>();
+ using Arg1 = projected<I1, Proj1>;
+ using Arg2 = projected<I2, Proj2>;
+ using Inner_result = indirect_result_of_t<Op2&(Arg1, Arg2)>;
+ using Outer_result = result_of_t<Op1&(T, InnerResult)>;
requires IndirectRegularInvocable<Op2, Arg1, Arg2>();
requires RegularInvocable<Op1, T, InnerResult>();
requires Assignable<T&, OuterResult>();
}
T inner_product(I1 first1, S1 last1, I2 first2, S2 last2, T init, Op1 op1 = Op1{},
Op2 op2 = Op2{}, Proj1 proj1 = Proj1{}, Proj2 proj2 = Proj2{});
```

Unlike the example given in our motivation, this indisputably communicates to both programmers and the compiler that `Arg1`

, `Arg2`

, `Inner_result`

and `Outer_result`

are not supposed to be changed by removing them from a position where a programmer can accidentally or deliberately provide an alternative type.

This document suggests three compounding proposals, provides minor motivations for each, and then discusses their benefits and drawbacks.

A requirements-body allows alias-declarations.

```
// Example 1
template <class T>
requires(T t) {
typename value_type_t<T>;
+ using value_type = value_type_t<T>;
{t + t} -> value_type;
}
void foo(T t) {}
// Example 2
template <class T>
requires(T t) {
typename value_type_t<T>;
+ using value_type = value_type_t<T>;
{t + t} -> value_type;
}
void foo(T t)
{
value_type bar{}; // ill-formed, value_type not in scope
}
// Example 3
template <class T>
requires(T t) {
typename value_type_t<T>;
+ using value_type = value_type_t<T>;
{t + t} -> value_type;
}
void foo(T t)
{
using value_type = value_type_t<T>;
value_type bar{}; // okay, value_type in scope
}
```

The key motivation for this proposal is its simplicity: an alias declared inside a *requirement-body* stays inside said *requirement-body*. Names are guaranteed not to leak into other namespaces, including function definitions and class specifications.

However, the author feels that this proposal, while simple, is very short sighted. A programmer that is specifying the requirements for a function or class is probably also writing said code. This proposal may simplify the verbosity of a requirements-body, but the programmer still needs to explicitly type out the type requirement and a second type alias in the function/class definition. Forcing the programmer to write out the same type (with the same name) multiple times is potentially error prone. We explore solutions to these in Proposals 2 and 3.

This proposal is currently being implemented using a branch from GCC trunk.

At present, it is possible to declare an alias inside the *requirement-body*, but the alias leaks outside of the enclosing body. This means that it isn’t possible to write such code:

```
template <typename T>
concept bool Foo = requires {
using Bar = double;
};
using Bar = int; // error: Bar already declared on line 3
```

This is under investigation, and can be found on GitHub.

Proposal 2 complements Proposal 1 by extending aliases declared inside the *requirement-body* to the function or class definition immediately following the body.

```
// Example 1
template <class T>
requires(T t) {
typename value_type_t<T>;
using value_type = value_type_t<T>;
{t + t} -> value_type;
}
void foo(T t)
{
value_type bar{}; // ok, value_type in scope
}
// Example 2
template <class T>
concept bool SequenceContainer = Container<X> && requires {
typename value_type_t<X>;
using value_type = value_type_t<X>;
...
};
template <SequenceContainer T>
struct Matrix {
Matrix(initializer_list<value_type> l); // ill-formed: value_type not in scope
};
```

This is a nice addition to the original proposal, as it allows for names declared inside a *requirement-body* to be declared only once. Names are still contained outside of the whole function/class scope, so there is no global pollution. This would be great in an associative container environment, for example:

```
template <class Key, class Value, class Compare, class A>
requires requires {
using key_type = Key;
using mapped_type = Value;
using value_type = pair<key_type, mapped_type>;
requires Allocator<A, value_type>;
}
class map {
// value_type, etc. already declared
};
```

The author foresees two drawbacks in this proposal:

- Aliases convenient for the
*requirement-body*but unused outside are available for accidental misuse. This is not any different to using an exposition-only template parameter, but it is a point worth considering. This could potentially be resolved by making all aliases private by default, and requiring a keyword such as export to precede the alias. - The aliases in the map definition might have private access, which may not always be desirable.

These drawbacks might not be compatible with the suggestion from Proposal 3 due to verbosity, and the author would like to open a discussion point.

As the first proposal is still being implemented, the technical difficulties of this proposal have not yet been assessed.

The author also believes that this is only half of a complete solution for the problems outlined in the initial proposal: it is still necessary to specify a *type-requirement* before using the *alias-declaration*.

Proposal 3 can be either an extension to Proposal 1 or Proposal 2, and allows a *type-requirement* to be made inline with the *alias-declaration*.

One might conclude that we can allow *alias-declarations* to immediately double as a *type-requirement*, but this doesn’t work, since the following would not be possible:

```
template <typename T>
requires requires(T t) {
using intializer_type = initializer_list<value_type_t<T>>;
}
void foo(T t);
void bar()
{
foo(vector<int>{}); // ill-formed: vector<int>::initializer_type doesn’t exist
}
```

The author instead proposes that an *alias-declaration* be prefixed with the `requires `

keyword to indicate it is both an alias, and a requirement; otherwise, it is just an alias. For example,

```
template <typename T>
requires requires(T t) {
+ requires using value_type = value_type_t<T>;
using initializer_type = initializer_list<value_type>;
requires Constructible<T, initializer_type>();
}
void foo(T t);
void bar()
{
foo(vector<int>{}); // ok: vector<int> satisfies the two requirements
foo(pair<int, int>{}); // ill-formed: pair<int, int>::value_type doesn’t exist
}
```

In this example, we see that `vector`

still conforms to the requirements outlined by `foo`

: namely, `typename value_type_t<T>`

, and `Constructible<T, initializer_type>`

(for some type `initializer_type`

). As stated previously, compatibility between this proposal and the previous proposal may be somewhat difficult.

No assessment has been made regarding technical feasibility at present.

In summary, we have looked at a proposal to allow for alias-declarations in requirement-bodies, a proposal extending the declarations to following functions and classes to reduce duplicate aliasing, and finally, a proposal that combines the aliasing and type-requirement into a single statement to further limit duplicate names.

Although there are some potential issues, the author remains hopeful that they can be ironed out.

Casey Carter and Eric Niebler entertained the initial idea and suggested potential alternative ways to implement this proposal, one of which became the actual implementation.

Tom Honermann and Casey also provided a proof reading of the document, and suggested several improvements.