Should there be an implicit deduction guide generated that allows for the instantiation of d<int> when constructing d{int{}, int{}}? What does the C++20 standard has to say about this?
#include <concepts>
struct b { b(auto, auto...) {} };
template<typename> struct d : b { using b::b; };
// clang ok, gcc nope, msvc nope
static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
Demo
GCC's error message:
<source>:5:51: error: class template argument deduction failed:
5 | static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
| ^
<source>:5:51: error: no matching function for call to 'd(int, int)'
<source>:4:27: note: candidate: 'template<class>
d()-> d< <template-parameter-1-1> >'
4 | template<typename> struct d : b { using b::b; };
| ^
<source>:4:27: note: template argument deduction/substitution failed:
<source>:5:51: note: candidate expects 0 arguments, 2 provided
5 | static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
| ^
<source>:4:27: note: candidate: 'template<class>
d(d< <template-parameter-1-1> >)-> d< <template-parameter-1-1> >'
4 | template<typename> struct d : b { using b::b; };
| ^
<source>:4:27: note: template argument deduction/substitution failed:
<source>:5:51: note: mismatched types 'd< <template-parameter-1-1> >' and 'int'
5 | static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
| ^
<source>:5:51: error: class template argument deduction failed:
<source>:5:51: error: no matching function for call to 'd(int, int)'
<source>:4:27: note: candidate: 'template<class>
d()-> d< <template-parameter-1-1> >'
4 | template<typename> struct d : b { using b::b; };
| ^
<source>:4:27: note: template argument deduction/substitution failed:
<source>:5:51: note: candidate expects 0 arguments, 2 provided
5 | static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
| ^
<source>:4:27: note: candidate: 'template<class>
d(d< <template-parameter-1-1> >)-> d< <template-parameter-1-1> >'
4 | template<typename> struct d : b { using b::b; };
| ^
<source>:4:27: note: template argument deduction/substitution failed:
<source>:5:51: note: mismatched types 'd< <template-parameter-1-1> >' and 'int'
5 | static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
| ^
<source>:5:20: error: template argument 1 is invalid
5 | static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MSVC's error message:
<source>(5): error C2641: cannot deduce template arguments for 'd'
<source>(5): error C2780: 'd<<unnamed-symbol>> d(b &&)':
expects 1 arguments - 2 provided
<source>(3): note: see declaration of 'd'
<source>(5): error C2780: 'd<<unnamed-symbol>> d(const b &)':
expects 1 arguments - 2 provided
<source>(3): note: see declaration of 'd'
<source>(5): error C2783: 'd<<unnamed-symbol>> d(_T0,_T1...)':
could not deduce template argument for '<unnamed-symbol>'
<source>(3): note: see declaration of 'd'
<source>(5): error C2780: 'd<<unnamed-symbol>> d(d<<unnamed-symbol>>)':
expects 1 arguments - 2 provided
<source>(4): note: see declaration of 'd'
<source>(5): error C2882: 'std': illegal use of namespace identifier in expression
The implicit deduction guides are described in [over.match.class.deduct]. First, p1.1 tells us how to generate an implicit deduction guide for each constructor of d (not including inherited constructors; see [namespace.udecl]/13). The constructors of d are the implicit copy and move constructors, which have the signatures
d(const d&);
d(d&&);
and thus result in the implicit guides
template <typename T> d(const d&) -> d<T>;
template <typename T> d(d&&) -> d<T>;
Obviously, these won't be viable in your case.
Then, p1.3 tells us to add the implicit copy deduction candidate, which has the form
template <typename T> d(d) -> d<T>;
which obviously isn't viable either.
The rest of paragraph 1 discusses how to generate deduction guides for aggregates and from inherited constructors. d isn't an aggregate, so that part doesn't apply. This is the most complicated part:
In addition, if
Cis defined and inherits constructors ([namespace.udecl]) from a direct base class denoted in the base-specifier-list by a class-or-decltypeB, letAbe an alias template whose template parameter list is that ofCand whose defining-type-id isB. IfAis a deducible template ([dcl.type.simple]), the set contains the guides ofAwith the return typeRof each guide replaced withtypename CC<R>::typegiven a class templatetemplate <typename> class CC;whose primary template is not defined and with a single partial specialization whose template parameter list is that of
Aand whose template argument list is a specialization ofAwith the template argument list ofA([temp.dep.type]) having a member typedeftypedesignating a template specialization with the template argument list ofAbut withCas the template.
So in this case B is just b, which means A has the form
template <typename T>
using A = b;
Now we have to determine whether A is deducible. According to [dcl.type.simple]/3,
A deducible template is either a class template or is an alias template whose defining-type-id is of the form
typenameopt nested-name-specifieropttemplateopt simple-template-idwhere the nested-name-specifier (if any) is non-dependent and the template-name of the simple-template-id names a deducible template.
Since b isn't a simple-template-id, the alias template A is not deducible. That means we can skip the rest; there are no deduction guides generated from inherited constructors. This is the intuitively obvious result, since b is non-dependent, so the constructors of b can't possibly help us deduce template arguments for d.
I have no idea why Clang accepts the code; maybe you should file a bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With