简体   繁体   中英

Type of non-type parameter in template template class is non deducible in C++14 but deducible in C++17

The title is a bit confusing but what I mean is this specific case:

template<class>
struct get_type_of_nontype;

template<class T, T Value, template<T> class Template>
struct get_type_of_nontype<Template<Value>> {
    using type = T;
};

So I can use it like this:

#include <type_traits>

template<int I>
class int_non_type {};

static_assert(
    std::is_same<typename get_type_of_nontype<int_non_type<0>>::type, int>::value,
    "T is deduced to be `int` as `template<T> class Template` is `template<int> class int_non_type`"
);

This works fine in C++17. In C++14 I get the following errors:

gcc 8:

<source>:5:8: error: template parameters not deducible in partial specialization:
struct get_type_of_nontype<Template<Value>> {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:5:8: note:         'T'

clang 7:

<source>:5:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct get_type_of_nontype<Template<Value>> {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:4:16: note: non-deducible template parameter 'T'
template<class T, T Value, template<T> class Template>
               ^

And then they both complain that struct get_type_of_nontype<int_non_type<0>> is incomplete, so typename get_type_of_non_type<int_non_type<0>>::type can't compile.

Why is this different between C++14 and C++17? Is this just a compiler bug? If not, is there a way to do this in C++14?

The Standard wording in [temp.deduct.type] paragraphs 13 and 14 changed. So yes, your example is invalid in C++14, but is allowed in C++17 thanks to a new language feature.

C++14:

A template type argument cannot be deduced from the type of a non-type template-argument .

[ Example:

 template<class T, T i> void f(double a[10][i]); int v[10][20]; f(v); // error: argument for template-parameter T cannot be deduced

-- end example ]

C++17:

When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value. [ Example:

 template<long n> struct A { }; template<typename T> struct C; template<typename T, T n> struct C<A<n>> { using Q = T; }; using R = long; using R = C<A<2>>::Q; // OK; T was deduced to long from the // template argument value in the type A<2>

-- end example ] The type of N in the type T[N] is std::size_t . [ Example:

 template<typename T> struct S; template<typename T, T n> struct S<int[n]> { using Q = T; }; using V = decltype(sizeof 0); using V = S<int[42]>::Q; // OK; T was deduced to std::size_t from the type int[42]

-- end example ]

[ Example:

 template<class T, T i> void f(int (&a)[i]); int v[10]; void g() { f(v); // OK: T is std::size_t }

-- end example ]

This seems a bit related to another C++17 template change: C++17 is the first version to allow placeholder types in non-type template parameters, as in template <auto Value> or template <auto* Ptr> . I'd expect compiler implementations would need some similar logic for supporting both of the two language features.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM