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?