When do two different class template partial specialization declarations match?
In the code below there are two partial specialization declarations:
S<constrain<T,has_accept_>, void>
S<constrain<T,has_visit_>, void>
constrain
is an alias template that equals to T
but is constrained using an enable_if
trick with the second parameter as a concept.
GCC considers that these two partial specialization are different but Clang and MSVC consider that they are equivalent and thus reject the code:
#include <type_traits>
#include <utility>
using namespace std;
template<class T,class=void>
struct has_accept
:false_type{};
template<class T>
struct has_accept<T,void_t<decltype(declval<const T&>().accept())>>
:true_type{};
template<class T,class=void>
struct has_visit
:false_type{};
template<class T>
struct has_visit<T,void_t<decltype(declval<const T&>().visit())>>
:true_type{};
//pre c++17 clang/MSVC fix: default argument of template
// used as template template argument not implemented yet
template<class T> using has_accept_ = has_accept<T>;
template<class T> using has_visit_ = has_visit<T>;
template<class T,template<class> class TT,class=enable_if_t<TT<T>::value>>
using constrain = T;
template<class T,class=void>
struct S
:false_type{};
template<class T>
struct S<constrain<T,has_accept_>,void> // (1)
:true_type{};
template<class T>
struct S<constrain<T,has_visit_>,void> // (2)
:true_type{}; // ==> MSVC and Clang: error (2) redefines (1)
I cannot find anything in the standard that would specify partial specialization equivalence. [temp.type] does not seem to apply here.
What does the standard say about partial specialization declaration equivalence?
This is CWG 1980, "Equivalent but not functionally-equivalent redeclarations":
In an example like
template<typename T, typename U> using X = T; template<typename T> X<void, typename T::type> f(); template<typename T> X<void, typename T::other> f();
it appears that the second declaration of
f
is a redeclaration of the first but distinguishable by SFINAE, i.e., equivalent but not functionally equivalent.Notes from the November, 2014 meeting:
CWG felt that these two declarations should not be equivalent.
This is still an active issue. gcc's behavior is more in line with the desire that these be different. [temp.alias]/2 and [temp.alias]/3 are the relevant transparency rules:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id.
which are in conflict here. In the simplified example from the issue, X<T, U>
is equivalent to T
- which means both declarations just have return type of void
- but substitution still applies, which doesn't exactly mean they're equivalent.
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