Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Imaginary deduction guides

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
like image 213
303 Avatar asked Dec 01 '25 16:12

303


1 Answers

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 C is defined and inherits constructors ([namespace.udecl]) from a direct base class denoted in the base-specifier-list by a class-or-decltype B, let A be an alias template whose template parameter list is that of C and whose defining-type-id is B. If A is a deducible template ([dcl.type.simple]), the set contains the guides of A with the return type R of each guide replaced with typename CC<R>​::​type given a class template

template <typename> class CC;

whose primary template is not defined and 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.dep.type]) having a member typedef type designating a template specialization with the template argument list of A but with C as 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-specifieropt templateopt simple-template-id

where 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.

like image 182
Brian Bi Avatar answered Dec 03 '25 08:12

Brian Bi