Oliv
Oliv

Reputation: 18081

Class template partial specialization equivalence

When do two different class template partial specialization declarations match?

In the code below there are two partial specialization declarations:

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?

Upvotes: 11

Views: 229

Answers (1)

Barry
Barry

Reputation: 303397

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.

Upvotes: 10

Related Questions