类模板部分特化等价

Oli*_*liv 11 c++ language-lawyer c++17

两个不同的类模板部分特化声明何时匹配?

在下面的代码中有两个部分特化声明:

  • S<constrain<T,has_accept_>, void>
  • S<constrain<T,has_visit_>, void>

constrain是一个别名模板,它等于T但是使用enable_if第二个参数作为概念的技巧进行约束.

GCC认为这两个部分专业化是不同的,但Clang和MSVC认为它们是等价的,因此拒绝代码:

#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)
Run Code Online (Sandbox Code Playgroud)

我在标准中找不到任何指定部分特化等价的东西.[temp.type]似乎不适用于此处.

标准对部分专业化声明等效性的说法是什么?

Bar*_*rry 10

这是CWG 1980,"等效但不是功能相当的重新声明":

在一个例子中

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();
Run Code Online (Sandbox Code Playgroud)

似乎第二个声明f是对第一个声明的重新声明,但是SFINAE可以区分,即等效但不是功能等同.

2014年11月会议记录:

CWG认为这两个声明不应该是等同的.

这仍然是一个活跃的问题.gcc的行为更符合这些不同的愿望.[temp.alias]/2[temp.alias]/3是相关的透明度规则:

模板id是指一个别名模板的专业化,它相当于通过其置换得到的相关联的类型的模板的参数模板参数类型ID别名模板.

但是,如果template-id是依赖的,则后续模板参数替换仍适用于template-id.

这里有冲突.在从问题的简单的例子,X<T, U>等同T-这意味着双方的声明只是返回类型的void-但替代仍然适用,这不正是意味着他们等同.