简体   繁体   English

使用void_t的多个SFINAE类模板专业化

[英]Multiple SFINAE class template specialisations using void_t

Are multiple class template specialisations valid, when each is distinct only between patterns involving template parameters in non-deduced contexts? 如果每个类的模板专长仅在非推导上下文中涉及模板参数的模式之间是唯一的,则多个类模板专长是否有效?

A common example of std::void_t uses it to define a trait which reveals whether a type has a member typedef called "type". std::void_t一个常见示例使用它来定义特征,该特征揭示类型是否具有称为“ type”的成员typedef Here, a single specialisation is employed. 在这里,采用单个专业化。 This could be extended to identify say whether a type has either a member typedef called "type1", or one called "type2". 这可以扩展到鉴定说一个类型是否有任何成员typedef称为“TYPE1”,或者一个名为“2型”。 The C++1z code below compiles with GCC, but not Clang. 下面的C ++ 1z代码使用GCC编译,但不使用Clang编译。 Is it legal? 合法吗

template <class, class = std::void_t<>>
struct has_members : std::false_type {};

template <class T>                      
struct has_members<T, std::void_t<typename T::type1>> : std::true_type {};

template <class T>                                                        
struct has_members<T, std::void_t<typename T::type2>> : std::true_type {};

There is a rule that partial specializations have to be more specialized than the primary template - both of your specializations follow that rule. 有一条规则,即部分专业化必须比主要模板更加专业化-您的两个专业都遵循该规则。 But there isn't a rule that states that partial specializations can never be ambiguous. 但是没有一条规则规定局部专业永远不会模棱两可。 It's more that - if instantiation leads to ambiguous specialization, the program is ill-formed. 更重要的是-如果实例化导致模棱两可的专业化,则程序格式错误。 But that ambiguous instantiation has to happen first! 但是,模棱两可的实例化必须首先发生!

It appears that clang is suffering from CWG 1558 here and is overly eager about substituting in void for std::void_t . 似乎这里的clang正在遭受CWG 1558的困扰,并且过于渴望用void代替std::void_t

This is CWG 1980 almost exactly: 这几乎是CWG 1980

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, ie, equivalent but not functionally equivalent. 似乎f的第二个声明是对第一个的重新声明,但可以被SFINAE区分,即等效但不等同于功能。

If you use the non-alias implementation of void_t : 如果您使用void_t的非别名实现:

template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;

then clang allows the two different specializations. 然后clang允许两个不同的专业化。 Sure, instantiating has_members on a type that has both type1 and type2 typedefs errors, but that's expected. 当然,在同时具有type1type2 typedef错误的类型上实例化has_members ,但这是预期的。

I don't believe it's correct, or at least, not if we instantiate has_members with a type that has both type1 and type2 nested, the result would be two specializations that are 我不认为这是正确的,或者至少不是,如果我们实例化具有嵌套的type1和type2的类型的has_members,结果将是两个专门化

has_members<T, void> 

which would not be valid. 这将是无效的。 Until the code is instantiated I think it's ok, but clang is rejecting it early. 在代码实例化之前,我认为还可以,但是clang会尽早拒绝它。 On g++, your fails with this use-case, once instantiated: 在g ++上,实例化后,您将无法使用此用例:

struct X
{
    using type1 = int;
    using type2 = double;
};

int main() {
    has_members<X>::value;
}

The error message is doesn't seem to describe the actual problem, but it at least is emitted: 错误消息似乎并未描述实际问题,但至少发出了该错误消息:

<source>:20:21: error: incomplete type 'has_members<X>' used in nested name specifier
     has_members<X>::value;
                     ^~~~~

If you instantiate it with a type that has only type1 or type2 but not both, then g++ compiles it cleanly. 如果使用仅具有type1 type2而不同时具有两者的类型实例化它,则g ++会干净地编译它。 So it's objecting to the fact that the members are both present, causing conflicting instantiations of the template. 因此,它反对成员都存在,从而导致模板的实例化冲突这一事实。

To get the disjunction, I think you'd want code like this: 为了获得分离,我想您需要这样的代码:

template <class, class = std::void_t<>>
struct has_members : std::bool_constant<false> {};

template <class T>
struct has_members<T, std::enable_if_t<
        std::disjunction<has_member_type1<T>, has_member_type2<T>>::value>> : 
    std::bool_constant<true> {};

This assumes you have traits to determine has_member_type1 and has_member_type2 already written. 这假定您具有确定已写入的has_member_type1和has_member_type2的特征。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM