类模板部分专业化等价

时间:2018-09-10 09:52:55

标签: 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)

我在标准中找不到指定部分专业化等效性的任何内容。 [temp.type]在这里似乎并不适用。

标准对部分专业化声明等价有何看法?

1 个答案:

答案 0 :(得分: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();
     

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

     

2014年11月会议的记录:

     

CWG认为这两个声明不应该等效。

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

  

template-id 指代别名模板的特殊化时,它等同于通过将其 template-arguments 替换为而获得的关联类型。别名模板的 type-id 中的> template-parameters

     

但是,如果 template-id 是相关的,则随后的模板参数替换仍然适用于 template-id

在这里有冲突。在此问题的简化示例中,X<T, U>T 等效-这意味着两个声明的返回类型均为void-但替换仍然适用,并不完全意味着它们等效