P1021R4
Mike Spertus, Symantec
mike_spertus@symantec.com
Timur Doumler
papers@timur.audio
Richard Smith
richardsmith@google.com
2019-06-17
Audience: Core Working Group
Filling holes in Class Template Argument Deduction
This paper proposes filling several gaps in Class Template Argument Deduction.
Document revision history
R0, 2018-05-07: Initial version
R1, 2018-10-07: Refocused paper on filling holes in CTAD
R2, 2018-11-26: Following EWG guidance, removed proposal for CTAD from partial template argument lists
R3, 2019-01-21: Add wording and change target to Core Working Group
R4, 2019-06-17: Updating wording in response to CWG comments
Rationale
As one of us (Timur) has noted when giving public presentations on using
 class template argument deduction, there are a significant number of 
cases where it cannot be used. This always deflates the positive 
feelings from the rest of the talk because it is accurately regarded as 
artificially inconsistent. In particular, listeners are invariably 
surprised that it does not work with aggregate templates, type aliases, 
and inherited constructors.
    We will show in this paper
that these limitations can be safely removed. Note that some of these 
items were intentionally deferred from
    C++17 with the intent of adding them in C++20. 
Class Template Argument Deduction for aggregates
We propose that Class Template Argument Deduction works for aggregate initialization as one
	shouldn't have to choose between having aggregates and deduction. This is well illustrated
	by the following example:
| C++17 | Proposed | 
|---|
| | template<classT>
 structPoint { T x; T y; };
   Point<double> p{3.0, 4.0};
 Point<double> p2{.x = 3.0, .y = 4.0};
 | 
 | | template<classT>
 structPoint { T x; T y; };
   Point p{3.0, 4.0};
 Point p2{.x = 3.0, .y = 4.0};
 | 
 | 
For the code on the right hand side to work in C++17, the user would 
have to write an explicit deduction guide. We believe that this is 
unnecessary and error-prone boilerplate and that the necessary deduction
 guide should be implicitly synthesized by the compiler  from the 
arguments of a braced  or designated initializer during aggregate 
initialization.
Algorithm
In the current Working Draft, an aggregate class is defined as a 
class with no user-declared or inherited constructors, no private or 
protected non-static data members, no virtual functions, and no virtual,
 private or protected base classes. While we would like to generate an 
aggregate deduction guide for class templates 
    that comply with these rules, we first need to consider the case 
where there is a dependent base
    class that may have virtual functions, which would violate the rules
 for aggregates. Fortunately,
    that case does not cause a problem because any deduction guides that
 require one or more arguments
    will result in a compile-time error after instantiation, and 
non-aggregate classes without
    user-declared or inherited constructors generate a zero-argument 
deduction guide anyway.
    Based on the above, we can safely generate an aggregate deduction 
guide for class templates
    that comply with aggregate rules.    
When P0960R0
 was discussed in Rapperswil, it was voted that in order to allow 
aggregate initialization from a parenthesized list of arguments, 
aggregate initialization should proceed as if there was a synthesized 
constructor. We can use the same approach to also synthesize the 
required additional deduction guide during class template argument
    deduction as follows:
    - Given a primary class template C, determine whether it satisfies all the conditions for an aggregate class ([dcl.init.aggr]/1.1 - 1.4).
- If yes, let  T_1, ...., T_n denote the types of the N elements ([dcl.init.aggr]/2) of the aggregate (which may or may not depend on its template arguments).
- Form a hypothetical constructor C(T_1, ..., T_N). 
- For every constructor argument of type T_i, if all types T_i ... T_n are default-constructible, add a default argument value zero-initializing the argument as if by T_i = {}.
- For the set of functions and function templates formed for 
[over.match.class.deduct], add an additional function template derived 
from this hypothetical constructor as described in 
[over.match.class.deduct]/1.
There is a slight complication resulting from subaggregates, and the 
fact that nested braces can be omitted when instantiating them:| structFoo { intx, y; };
 structBar { Foo f; intz; };
 Bar bar{1, 2};   
 | 
In this case, we have two initializers, but they do not map to the two elements of the aggregate type Bar, instead initializing the sub-elements of the first subaggregate element of type Foo. 
For complicated nested aggregates, there are potentially many 
different combinations of valid mappings of initializers to subaggregate
 elements. It would be unpractical to create hypothetical constructors 
for all of those combinations. Additionally, whether or not an aggregate
 type has subaggregate elements may depend on the template arguments:
| template<typenameT>
 structBar { T f; intz; };  
 | 
This information is not available during class template argument deduction, because for this we first need to deduce T.
 We therefore propose simply to avoid all of these problems by 
prohibiting the omission of nested braces when performing class template
 argument deduction.
Class Template Argument Deduction for alias templates
While Class Template Argument Deduction makes type inferencing easier 
when constructing classes,
it doesn't work for type aliases, penalizing the use of type aliases and
 creating unnecessary inconsistency.We propose allowing Class Template 
Argument Deduction for type aliases as in the following example.
    | vector | pmr::vector (C++17) | pmr::vector (proposed) | 
|---|
    | | vector v = {1, 2, 3};
 vector v2(cont.begin(), cont.end());
 | 
 | | pmr::vector<int> v = {1, 2, 3};
 pmr::vector<decltype(cont)::value_type> v2(cont.begin(), cont.end());
 | 
 | | pmr::vector v = {1, 2, 3};
 pmr::vector v2(cont.begin(), cont.end());
 | 
 | 
    pmr::vector also serves to illustrate another interesting case. While one might
    be tempted to write
    | pmr::vector pv({1, 2, 3}, mem_res); 
 | 
    this example is ill-formed by the normal rules of template argument deduction because
    pmr::memory_resource fails to 
    deduce pmr::polymorphic_allocator<int> in the  second argument. 
    While this is to be expected, one suspects that had class template argument deduction
        for alias templates been around when pmr::vector was being designed,
        the committee would have considered allowing such an inference as safe and useful in this context.
        If that was desired, it could easily have been achieved 
        by indicating that the pmr::allocator template parameter should be considered non-deducible:
    
| namespacepmr {
   template<typenameT>
   usingvector = std::vector<T, type_identity_t<pmr::polymorphic_allocator<T>>>; 
 }
 pmr::vector pv({1, 2, 3}, mem_res); 
 | 
 Finally, in the spirit of alias templates being simply an alias for the type, we do not propose
allowing the programmer to write explicit deduction guides specifically for an alias
template.
Algorithm
For deriving deduction guides for the alias templates from guides in the class, we use the following approach (for which we are very grateful for the invaluable assistance of Richard Smith):
- Deduce template parameters for the deduction guide by deducing the right hand side of
    the deduction guide from the alias template. We do not require that this deduces all the template
    parameters as nondeducible contexts may of course occur in general
- Substitute any deductions made back into the deduction guides. Since the previous step may not
    have deduced all template parameters of the deduction guide, the new guide may have template
    parameters from both the type alias and the original deduction guide.
- Derive the corresponding deduction guide for the alias template by 
    deducing the alias from the result type. Note that a constraint may be necessary
    as whether and how to deduce the alias from the result type may depend on the
    actual argument types.
- The guide generated from the copy deduction guide should be given
    the precedence associated with copy deduction guides during overload resolution
Let us illustrate this process with an example. Consider the following example:| template<classT> usingP = pair<int, T>;
 | 
Naively using the deduction guides from pair is not ideal because they
    cannot necessarily deduce objects of type P even
    from arguments that should obviously work, like P({}, 0). However,
    let us apply the above procedure. The relevant deduction guide is
| template<classA, classB> pair(A, B) -> pair<A, B>
 | 
Deducing (A, B) from (int, T) yield int for A
    and T for B. Now substitute back into the deduction guide to get
    a new deduction guide
| template<classT> pair(int, T) -> pair<int, T>;
 | 
Deducing the template arguments for the alias template from this gives us the following deduction guide for the alias template 
| template<classT> P(int, T) -> P<T>;
 | 
A repository of additional expository materials and worked out examples used in the refinement of this algorithm 
    is maintained online.
Deducing from inherited constructors
In C++17, deduction guides (implicit and explicit) are not inherited when constructors are inherited.
	According to the C++ Core Guidelines C.52,
 you should “use inheriting constructors to import constructors into a 
derived class that does not need further explicit initialization”. As 
the creator of such a thin wrapper has not asked in any way for the 
derived class to behave differently under construction, our experience 
is that users are
surprised that construction behavior changes:
| | template<typenameT> structCallObserver requires Invocable<T> { 
   CallObserver(T &&) : t(std::forward<T>(t)) {}
   virtualvoidobserveCall() { t(); }
   T t;
 };
   template<typenameT> structCallLogger : publicCallObserver<T> { 
   usingCallObserver<T>::CallObserver; 
   virtualvoidobserveCall() override { cout << "calling"; t();  }
 };
 | 
 | 
| C++17 | Proposed | 
|---|
| | CallObserver observer([]() { }); 
 CallLogger<> logger([]() { });
 | 
 | | CallObserver observer([]() { }); 
 CallLogger logger([]() { }); 
 | 
 | 
Note that inheriting the constructors of a base class must include 
inheriting all the deduction guides, not just the implicit ones. As a 
number of standard library
writers use explicit guides to behave “as-if” their classes were defined
 as in the standard, such internal implementation details
details would become visible if only the internal guides were inherited.
 We of course use the same algorithm
    for determining deduction guides for the base class template as 
described above for alias templates.
Wording
In over.match.class.deduct, append to paragraph 1 as follows:
  
    - […]
- For each deduction-guide, […]
      
        - The template parameters, if any, […]
- The return type […]
 
In addition, if C satisfies the conditions for an aggregate class with the assumption that any dependent base class has no virtual functions and no virtual base classes, let e1, ... en be the elements of C ([dcl.init.aggr]). 
		If there is at least one such element, and the initializer is a non-empty braced-init-list or parenthesized expression-list, the set contains an additional function template, called the aggregate deduction candidate, defined as follows. Let x1, ..., xm 
		be the elements of the initializer-list, designated-initializer-list, or expression-list. The aggregate deduction candidate is derived as above from a hypothetical constructor C(T1, ..., Tm), where Ti is the declared type of the element ei of C which would be explicitly initialized by xi as described in [dcl.init.aggr].
	  
      In addition, if C inherits constructors (namespace.udecl 9.8) 
        from a base
        class denoted by a simple-template-id B, 
          the set contains the functions
      and function templates formed from an
        alias template (over.match.alias.deduct) whose template parameters are those of C
        and whose simple-template-id is B.
Add a new subsubsection after over.match.class.deduct titled “over.match.alias.deduct”
When resolving a placeholder for a deduced class type (dcl.spec.auto 9.1.7.5) where the
	template-name names an alias template A 
	whose defining-type-id is a simple-template-id
    naming a specialization of a class template C with 
    template argument list L, a
	set of functions and function templates is formed as follows. For each function or function
	template f formed for C by the process in 
    (over.match.class.deduct 11.3.1.8), form a function or function
	template f' according to the following procedure:
	- Follow the procedure below for deducing the arguments of the result type of f
			from C<L> with the exception that deduction does
	not fail if not all template arguments are deduced.
- Form f' as follows:
		- The parameters and return type are constructed by  substituting 
			 into 
			the parameters (including default arguments) and return type of f
			the deductions
			of all template parameters that were successfully deduced in the deduction of 
			the result type of f.
- The template parameters consist of those template parameters of f or A
			that appear in the parameters and return type constructed in the previous step.
- The associated constraints (temp.constr.decl 12.4.2) are the conjunction of the associated constraints
			of f and a constraint that is 
            satisfied if and only if the arguments of A
			are deducible (see below) from the return type.
- If f is a copy deduction candidate (over.match.class.deduct 11.3.1.8), then f'
                is considered to be so as well.
- 
			If f was generated from a deduction-guide (over.match.class.deduct 11.3.1.8), then f'
                is considered to be so as well.
- If f was generated from a constructor or deduction-guide
				with an explicit-specifier, then
                    f' is considered to be so as well.
 
The arguments of a template A are said to be deducible from a type T if, given a class templatetemplate <typename> class AA;
    with a single partial specialization whose template parameter list is that of A and whose template argument list is a specialization of A with the template argument list of A (temp.deduct.type 12.9.2.5), AA<T> matches the partial specialization.
	Initialization and overload resolution are performed as described in 
    (dcl.init 9.3) and (over.match.ctor 11.3.1.3), 
    (over.match.copy 11.3.1.4), or (over.match.list 11.3.1.7) (as
appropriate for the type of initialization performed) for an object of a hypothetical class type, where the
selected functions and function templates are considered to be the constructors of that class type for the
purpose of forming an overload set, and the initializer is provided by the context in which class template
argument deduction was performed. As an exception, the first phase in 11.3.1.7 (considering initializer-list
constructors) is omitted if the initializer list consists of a single expression of type cv U,
	where U is a specialization of C or a class derived from a specialization of C.
	If the function or 
	function template was generated from a constructor or deduction-guide
that had an explicit-specifier, each such notional constructor is considered to
	have that same explicit-specifier. All such notional constructors are considered 
	to be public members of the hypothetical class type. [Note: The constraint ensures that
	a specialization of A can be deduced from the return type. — end note]
	
	[Example:
	template <class T, class U> struct C {
	  C(T, U);
	};
	template<class T, class U>
	C(T, U) -> C<T, std::type_identity_t<U>>;
	
	template<class V>
	using A = C<V *, V *>;
	
	int i{};
	double d{};
	A a1(&i, &i); // Deduces A<int>
	A a2(i, i); // Error: cannot deduce V * from i
	A a3(&i, &d); // Error: cannot deduce alias template from C<int *, double *> 
	
    Possible exposition only implementation of the above procedure:
	        //The following concept ensures a specialization of A is deduced
        template <class> class AA;
        template <class V> class AA<A<V>> { };
        template <class T> concept deduces_A = requires { sizeof(AA<T>); };
        
        // f is formed (over.match.class.deduct 11.3.1.8) from the deduction-guide of C
        template<class T, class U> auto f(T, U) -> C<T, std::type_identity_t<U>>;
	// Deducing arguments for C<T, std::type_identity_t<U>> from C<V *, V*> deduces T as V *
	
	// f' is obtained by transforming f as described by the above procedure
	template<class V, class U> 
	auto f_prime(V *, U) -> C<V *, std::type_identity_t<U>>
	  requires deduces_A<C<V *, std::type_identity_t<U>>>;
	
	— end example]
		
Insert a bullet after over.match.best/1.9 as follows
	
	- F1 is generated from class template argument deduction (over.match.class.deduct 11.3.1.8) for
		a class D, F2 is generated from inheriting constructors from
		a base class of D, and for all arguments the corresponding
		parameters of F1 and F2 have the same type, or if not that,