---
breaks: false
---
<style type="text/css">
ins { color: #080 }
s { color: #F00 }
blockquote.part { color: inherit !important }
tbody code { background-color: inherit !important }
</style>
<table><tbody>
<tr><th>Doc. no.:</th> <td>P1158R0</td></tr>
<tr><th>Date:</th> <td>2018-07-11</td></tr>
<tr><th>Audience:</th> <td>EWG</td></tr>
<tr><th>Reply-to:</th> <td>Zhihao Yuan <zy at miator dot net></td></tr>
</tbody></table>
# Concept-defined placeholder types
```c++
BidirectionalIterator T;
T it = foo();
```
## Motivation
We have three kinds of placeholder types to define variables -- `auto`, `decltype(auto)`, and `ClassTemplate`. The former two deduce arbitrary types, and the last one deduces specializations. So that we can constrain the deduced type to be a pointer type with `auto*`, or we can constrain the deduced type to be a specialization of `std::iterator`, but we are not able to constrain the deduced type to be an `Iterator`. We need the functionality to deduce types that satisfy the constraints expressed with Concepts.
## Introduction
The following example from P0915R0[^p0915] shows a real issue when missing such a kind of constraints when declaring variables in generic code:
```c++
template <typename Producer>
void uploadToGPU(Producer& producer)
{
auto item = producer.next();
gpuMemcpy(dst, &item, sizeof(item)); // potentially UB
}
```
A quick and dirty fix is to use `static_assert`:
```c++
template <typename Producer>
void uploadToGPU(Producer& producer)
{
auto item = producer.next();
static_assert(StandardLayoutType<decltype(item)>);
gpuMemcpy(dst, &item, sizeof(item));
}
```
which comes with a considerable distance between our idea and the code we written. Here is a different workaround:
```c++
template <typename Producer>
void uploadToGPU(Producer& producer)
{
auto item = producer.next();
StandardLayoutObject(item);
gpuMemcpy(dst, &item, sizeof(item));
}
template <StandardLayoutType T>
void StandardLayoutObject(T) {};
```
which is getting close. Look at this `T`, it is
- used in place of a type for deduction,
- constrained.
It has all the functionalities we are [motivated](#Motivation) to add. The only issue is that it is a _constrained-parameter_, declared only in _template-parameter-list_. Can we introduce such a `T` in other places?
```c++
template <typename Producer>
void uploadToGPU(Producer& producer)
{
StandardLayoutType T;
T item = producer.next();
gpuMemcpy(dst, &item, sizeof(item));
}
```
That is the feature we propose -- a _constrained-type-name_.
## Design Decisions
Model everything after _constrained-parameter_.
1. In a _constrained-type-name_'s scope, the _constrained-type-name_ can get involved in deduction multiple times, and must deduce to the same type.
```c++
template <Iterator T>
void foo(T, std::move_iterator<T>);
Iterator T;
T it = begin(x);
// ...
std::move_iterator<T> i2 = ...;
```
An rvalue reference to _constrained-type-name_ is not a forwarding reference, because in
```c++
template <Copyable T>
void foo(T&&);
```
The `foo` is invented rather than intended. Class template argument deduction can model this intention better:
```c++
Copyable T;
T &&a = 'a';
template <Copyable T>
struct Foo
{
Foo(T&&);
};
Foo('a')
```
A _constrained-type-name_ is not deduced from a discarded statement in a template entity.
```c++
template <auto>
auto foo()
{
Copyable T;
if constexpr (cond)
T v = ...;
else
T u = ...;
std::aligned_storage_t<sizeof(T), alignof(T)> s;
...
}
```
In the code above, `T` is only deduced from the _true_ branch.
2. A _constrained-type-name_ is a concrete type in non-deduced context after it has been deduced; if not deduced, the program is ill-formed.
```c++
template <Copyable T>
void foo(std::array<char, sizeof(T)> a);
foo({ 'a', 'b' }); // ill-formed
Copyable T;
std::array<char, sizeof(T)> a; // ill-formed
```
A _constrained-type-name_ is not deduced from a local class scope that is nested to the scope where the _constrained-type-name_ is declared.
```c++
Copyable T;
auto f = [](char* p, size_t sz)
{
T x = foo(p, sz); // ill-formed if T is not deduced elsewhere
};
```
3. Deducing a _constrained-type-name_ must use the copy-initialization syntax. The code that involves _constrained-type-name_ cannot be visually distinguished from initializations using concrete types, but we can limit the points of deduction by enforcing an `=` in front of the _initializer-clause_. Meanwhile, it simplifies the semantics because the function parameters are also copy-initialized when deducing the function template parameters.
```c++
template <Iterator T>
void foo(T);
char s[] = "";
foo(s);
Iterator T;
T p = s;
T np(nullptr); // initializing char*
foo(T(nullptr)); // ok
T ep = nullptr; // ill-formed, T has been deduced
```
4. A _constrained-type-name_ should only appear in block scope and have no linkage. In class scopes, there is a complete analysis[^n3897] to show why we will not have placeholder types on class members. In namespace scopes, _constrained-type-name_ itself will have ODR issues as soon as it gains linkage.
## Technical Description
> _simple-type-specifier_:<br/>
> [...]<br/>
> `auto`<br/>
> _decltype-specifier_<br/>
> ++_constrained-type-name_++<br/>
>
> ++_constrained-type-name_:++<br/>
> ++_identifier_++
> _declaration_:<br/>
> [...]<br/>
> _attribute-declaration_<br/>
> ++_constrained-type-declaration_++<br/>
>
> ++_constrained-type-declaration_:++<br/>
> ++_constrained-type-declarator-list_ `;`++<br/>
>
> ++_constrained-type-declarator-list_:++<br/>
> ++_constrained-type-declarator_++<br/>
> ++_constrained-type-declarator-list_ `,` _constrained-type-declarator_++<br/>
>
> ++_constrained-type-declarator_:++<br/>
> ++_qualified-concept-name_ _identifier_++<br/>
>
> A _constrained-type-declaration_ declares each _identifier_ that a _constrained-type-declarator_ ends with to be a _constrained-type-name_. A _constrained-type-declaration_ shall only appears at block scope.
>
> A _constrained-type-name_ is looked up as a _type-name_ in its scope. In an initializing declaration of a variable, Let $D$ be a sequence of the _decl-specifiers_ that are _type-specifiers_ in the _decl-specifier-seq_. If $D$ mentions a _constrained-type-name_ defined in the same scope and $D$ followed by the _declarator_ can form a function template argument in deduced context, this is a _constrained initializing declaration_, and after each _declarator_ there shall be an _initializer-clause_ followed by `=`. Let $I_1$, $I_2$, ..., $I_n$ be the list of _declarators_, $E_1$, $E_2$, ..., $E_n$ be those _initializer-clauses_ in order, $C_1$, $C_2$, ..., $C_m$ be the _constrained-type-declarators_ of all the mentioned _constrained-type-names_. Given the following invented class template,
>
> `template<`$C_1$, $C_2$, ..., $C_m$`>`<br/>
> `struct f { f(`$D$ $I_1$, $D$ $I_2$, ..., $D$ $I_n$`); };`
>
> the _constrained-type-names_ are _bounded_ to be the types of the template arguments that are deduced from the call `f(`$E_1$, $E_2$, ..., $E_n$`)` as an unevaluated operand. If the class template or the call is ill-formed, the program is ill-formed.
>
> Any use of a _constrained-type-name_ refers to the bounded type; if the _constrained-type-name_ is not bounded at the point of use or bounded to more than one type, the program is ill-formed.
Examples (not concerning forwarding references):
Given
```c++
Copyable T;
Iterator A, Copyable B;
```
for
```c++
T a[] = "meow";
```
we form
```c++
template<Copyable T>
void f(T a[]);
```
Calling `f("meow")` deduces `T` to `char`, so the original declaration becomes
```c++
char a[] = "meow";
```
For
```c++
tuple<T*, int> b;
tuple<A, B> &r = b, c = tuple(a, 3);
```
we form
```c++
template<Iterator A, Copyable B>
void f(tuple<A, B> &r, tuple<A, B> c);
```
Calling `f(b, tuple(a, 3))` gives `A = char*` and `B = int`.
## Comparing with Other Proposals
There have been a few proposals that can address the motivation of this paper: Concepts TS[^n4674], in-place syntax[^p0745], constrained `auto`[^p0915], and YAACD[^p1141]. The in-place syntax extends the _constrained-type-specifier_ from the Concepts TS by allowing optional in-place type names, so I will call their common parts "_constrained-type-specifier_" and discuss the "in-place syntax" separately. YAACD part 1 and constrained `auto` differ only in syntax so that I will group them into "constrained `auto`." The part 2 makes the syntax compatible with the simple case (a single _decl-specifier_ that is a _qualified-concept-name_) of Concepts TS so that I will skip this part.
The _constrained-type-specifier_ has two set of rules for deduction, one for simple cases,
```c++
Copyable &&a = foo();
```
and one for complex cases:
```c++
tuple<Iterator, Copyable> c = make_tuple(a, 3);
```
In simple cases, deduction result backfills the type for the variable, so `&&` is treated as a forwarding reference; in complex cases, deduction result backfills the template parameters, so `&&` is treated as an rvalue reference. This proposal, "concept-defined placeholder types" do not make this distinction and stick with the latter rule. The other constrained `auto` proposals only handle the simple cases and use the former rule.
The in-place syntax and concept-defined placeholder types can naturally express consistent binding[^p0694],
```c++
// in-place syntax
Copyable{T} a = foo();
tuple<Copyable{T}> b = bar();
// this paper
Copyable T;
T a = foo();
tuple<T> b = bar();
```
_constrained-type-specifier_ and constrained `auto` have no such flexibility. The introduced type names can serve other purposes, for example, in
```c++
Copyable{T} &&a = foo();
```
this `T` can replace `std::remove_cvref_t<decltype(a)>`. The difference is that, in this proposal, introducing these type names are mandatory. By doing so, we solved, simultaneously, the confusion (examples in (p)3.6[^p0694])
>> Independent resolution breaks the fundamental equivalence of the notations.
and the dilemma[^p0956]
>> We need a way of expressing "same type" for two uses of a concept.<br/>
>> We need a way of saying "different type" for two uses of a concept.
raised in Bjarne's papers.
The concept-defined placeholder types require a specific form of initialization (copy-initialization) to trigger deduction. But it is hard to say whether it is a caveat or a feature, considering that none of the proposals attempt class template argument deduction from partially-specialized template argument lists[^p1021], which is implied by direct-initialization.
## Extensions
If we add multi-argument _constrained-parameters_[^p1157],
```c++
template <EqualityComparableWith T U>
void foo(T const& a, U const& b)
{
if (a == b) // must be valid
```
we should also allow declaring multiple _constrained-type-names_ that satisfy a multi-parameter concept at once:
```c++
EqualityComparableWith T U;
T a = /* ... */;
U b = /* ... */;
if (a == b) // must be valid
```
We may also want to add parameter pack support.
## References
[^p0915]: Romeo, Vittorio, and John Lakos. P0915R0 _Concept-constrained auto_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0915r0.html>
[^n3897]: Voutilainen, Ville. N3897 _Auto-type members_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3897.html>
[^n4674]: Sutton, Andrew. N4674 _Working Draft, C++ extensions for Concepts_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4674.pdf>
[^p0745]: Sutter, Herb. P0745R1 _Concepts in-place syntax_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0745r1.pdf>
[^p1141]: Voutilainen, Ville, et al. P1141R0 _Yet another approach for constrained declarations_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1141r0.html>
[^p0694]: Stroustrup, Bjarne. P0694R0 _Function declarations using concepts_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0694r0.pdf>
[^p0956]: Stroustrup, Bjarne. P0956R0 _Answers to concept syntax suggestions_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0956r0.pdf>
[^p1157]: Yuan, Zhihao. P1157R0 _Multi-argument constrained-parameter_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1157r0.html>
[^p1021]: Spertus, Mike. P1021R0 _Extensions to Class Template Argument Deduction_.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1021r0.html>