两个不同的类模板局部特化声明何时匹配?
在下面的代码中,有两个部分专业化声明:
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]在这里似乎并不适用。
标准对部分专业化声明等价有何看法?
答案 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
-但替换仍然适用,并不完全意味着它们等效。