CTAD with aggregate initialization of nested templated base class

106 Views Asked by At

Due to the aggregate initialization rules, I would expect to be able to initialize d::q by passing an object of its direct base class. However, Clang and MSVC seem to reject this code.

#include <concepts>

template<typename> struct s {};
template<typename> struct b {
    template<typename T>
    struct p : s<T> {};
};
template<typename U>
struct d : b<U> {
    template<typename T>
    using t = b<U>::template p<T>;
    template<typename T>
    struct q : t<T> {};
    static constexpr auto v = q{t<U>{}};
};
static_assert(std::same_as<decltype(d<int>::v), d<int>::q<int> const>);

Shouldn't there be some kind of implicit deduction guide generated for d::q that would allow for the construction of q{t<U>{}}? What does the C++20 standard has to say about this? What specific mechanism should kick into action here?

Also, what would be a possible workaround for this issue (constructing d::q with the use of CTAD) that makes the code compile on the latest stable version of all 3 compilers (Clang, GCC, MSVC) without making things overly convoluted? Providing a user-defined deduction guide for d::q seems to work for MSVC but Clang complains about a non-deducible template parameter.

Demo


Clang's error message:

<source>:15:31: error: no viable constructor or deduction guide for deduction of
template arguments of 'q'
   15 |     static constexpr auto v = q{t<U>{}};
      |                               ^
<source>:17:37: note: in instantiation of template class 'd<int>' requested here
   17 | static_assert(std::same_as<decltype(d<int>::v), d<int>::q<int> const>);
      |                                     ^
<source>:14:12: note: candidate template ignored: could not match 'q' against 'p'
   14 |     struct q : t<T> {};
      |            ^
<source>:14:12: note: candidate function template not viable: requires 0 arguments,
but 1 was provided
<source>:17:62: error: expected '(' for function-style cast or type construction
   17 | static_assert(std::same_as<decltype(d<int>::v), d<int>::q<int> const>);
      |                                                           ~~~^
<source>:17:64: error: expected expression
   17 | static_assert(std::same_as<decltype(d<int>::v), d<int>::q<int> const>);
      | 

MSVC's error message:

<source>(15): error C2641: cannot deduce template arguments for 'd<int>::q'
<source>(15): note: the template instantiation context (the oldest one first) is
<source>(17): note: see reference to class template instantiation 'd<int>' being
compiled
<source>(15): error C2780: 'd<U>::q<T> d<int>::q(void)': expects 0 arguments -
1 provided
<source>(14): note: see declaration of 'd<int>::q'
<source>(15): error C2784: 'd<U>::q<T> d<int>::q(d<U>::q<T>)': could not deduce
template argument for 'd<U>::q<T>' from 'b<U>::p<U>'
        with
        [
            U=int
        ]
<source>(14): note: see declaration of 'd<int>::q'
<source>(15): error C2783: 'd<U>::q<T> d<int>::q(b<<unnamed-symbol>>::p<T>)':
could not deduce template argument for 'T'
<source>(14): note: see declaration of 'd<int>::q'
<source>(15): error C2119: 'v': the type for 'auto' cannot be deduced from an
empty initializer
<source>(17): error C2607: static assertion failed
<source>(17): note: the concept 'std::same_as<const int,const d<int>::q<int>>'
evaluated to false
C:/data/msvc/14.38.33133/include\concepts(35): note: 'const int'
and 'const d<int>::q<int>' are different types
1

There are 1 best solutions below

6
user12002570 On

what would be a possible workaround for this issue that makes the code compile on all 3 compilers (Clang, GCC, MSVC) without making things overly convoluted?

You can default the template parameter for q as shown below:

//------------------vvv---->use default
template<typename T = U>
struct q : t<T> {};

Now also the program works for all compilers.

Working demo