GCC and clang disagree about this code.
#include <type_traits>
template <typename T, template <typename...> typename Tpl>
struct storage {
using type_t = T;
template <typename... Args>
using storage_tpl = Tpl<Args...>;
};
template <typename T, template <typename...> typename>
struct F{
constexpr static int x = 1;
};
template <typename T >
struct F<T, std::void_t>{
constexpr static int x = 2;
};
int f() {
using S = storage<int, std::void_t>;
static_assert(F<int, S::storage_tpl>().x == 2);
return F<int, S::storage_tpl>().x;
}
According to clang S::storage_tpl
is not std::void_t
; of consequence it selects the primary template F instead of the partial specialization and thus the assert.
At first glance, it looks like GCC is right because it understands that the nested template is just an alias for std::void_t
but maybe it is too smart and the standard requires that S::storage_tpl
and std::void_t
must be two different templates.
Who is right?
It looks like this is unspecified at the moment and h/t to T.C. it looks like this is covered by CWG defect report 1286 which says:
Issue 1244 was resolved by changing the example in 17.5 [temp.type] paragraph 1 from
template<template<class> class TT> struct X { }; template<class> struct Y { }; template<class T> using Z = Y<T>; X<Y> y; X<Z> z;
to
template<class T> struct X { }; template<class> struct Y { }; template<class T> using Z = Y<T>; X<Y<int> > y; X<Z<int> > z;
In fact, the original intent was that the example should have been correct as written; however, the normative wording to make it so was missing. The current wording of 17.6.7 [temp.alias] deals only with the equivalence of a specialization of an alias template with the type-id after substitution. Wording needs to be added specifying under what circumstances an alias template itself is equivalent to a class template.
and proposed the following resolution:
Add the following as a new paragraph following 17.6.7 [temp.alias] paragraph 2:
When the type-id in the declaration of alias template (call it A) consists of a simple-template-id in which the template-argument-list consists of a list of identifiers naming each template-parameter of A exactly once in the same order in which they appear in A's template-parameter-list, the alias template is equivalent to the template named in the simple-template-id (call it T) if A and T have the same number of template-parameters. [Footnote: This rule is transitive: if an alias template A is equivalent to another alias template B that is equivalent to a class template C, then A is also equivalent to C, and A and B are also equivalent to each other. —end footnote] [Example:
template<typename T, U = T> struct A; template<typename V, typename W> using B = A<V, W>; // equivalent to A template<typename V, typename W> using C = A<V>; // not equivalent to A: // not all parameters used template<typename V> using D = A<V>; // not equivalent to A: // different number of parameters template<typename V, typename W> using E = A<W, V>; // not equivalent to A: // template-arguments in wrong order template<typename V, typename W = int> using F = A<V, W>; // equivalent to A: // default arguments not considered template<typename V, typename W> using G = A<V, W>; // equivalent to A and B template<typename V, typename W> using H = E<V, W>; // equivalent to E template<typename V, typename W> using I = A<V, typename W::type>; // not equivalent to A: // argument not identifier
—end example]
but there are issues with this resolution and the defect report is still active.
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