[英]compiler differences when using std::enable_if in return type of templated member function
I tried to define two "overloads" for a method in struct Node
that, depending on the struct template parameter invoke different behaviours.我试图为struct Node
中的一个方法定义两个“重载”,根据结构模板参数调用不同的行为。
I am aware that I could use other methods (like if constexpr
or whatever)..我知道我可以使用其他方法(例如if constexpr
或其他方法)。
My concern is that I "tricked" the gcc 9.0.0 experimental 20180919 into compiling this, but all the other recent compilers available in wandbox (permlink below) do not.我担心的是,我“欺骗”了 gcc 9.0.0 实验 20180919 来编译它,但是 wandbox(下面的 permlink)中可用的所有其他最新编译器都没有。 I suppose gcc8 and clang6/7 do the right thing here?我想 gcc8 和 clang6/7 在这里做正确的事?
Thanks for your help!谢谢你的帮助!
#include <iostream>
#include <type_traits>
enum laziness { lazy, nonlazy };
enum logic { AND, OR };
template<laziness LA, logic LO>
struct Node{
template<typename = void>
std::enable_if_t<LA==lazy> printLaziness () const {
std::cout << "lazy" << std::endl;
}
template<typename = void>
std::enable_if_t<LA==nonlazy> printLaziness () const {
std::cout << "non-lazy" << std::endl;
}
};
int main () {
Node<lazy, AND> x{};
x.printLaziness();
Node<nonlazy, OR> y{};
y.printLaziness();
}
https://wandbox.org/permlink/d2IaWwt9GJn31Wmq https://wandbox.org/permlink/d2IaWwt9GJn31Wmq
The code is ill-formed, and that experimental gcc 9.0.0 is wrong to ignore it.代码格式错误,实验性 gcc 9.0.0 忽略它是错误的。
A key thing to understand about the SFINAE rule is that it applies when an invalid type or expression is formed while substituting a parameter of a function template into default template arguments for other function template parameters or into the function type, as described in the introduction of section [temp.deduct] ;理解 SFINAE 规则的一个关键点是,它适用于在将函数模板的参数替换为其他函数模板参数的默认模板参数或函数类型的同时形成无效类型或表达式时,如介绍中所述[temp.deduct]部分; or when determining whether a partial specialization of a class template matches or which partial specialization is most specialized.或者在确定类模板的部分特化是否匹配或哪个部分特化最特化时。 But in your code, the invalid type is formed while substituting the parameter LA
of the class template, not a parameter of the function templates.但是在您的代码中,在替换类模板的参数LA
时形成了无效类型,而不是函数模板的参数。
Also relevant is paragraph [temp.inst]/2 :同样相关的是[temp.inst]/2 段:
The implicit instantiation of a class template specialization causes类模板特化的隐式实例化导致
the implicit instantiation of the declarations, but not of the definitions, of the non-deleted class member functions, member classes, scoped member enumerations, static data members, member templates, and friends;未删除的类成员函数、成员类、作用域成员枚举、静态数据成员、成员模板和朋友的声明的隐式实例化,但不是定义的隐式实例化; and和
the implicit instantiation of the definitions of deleted member functions, unscoped member enumerations, and member anonymous unions.已删除成员函数、无作用域成员枚举和成员匿名联合的定义的隐式实例化。
The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifier s of the class member functions.类模板特化的隐式实例化不会导致类成员函数的默认参数或noexcept-specifier的隐式实例化。
Since the definitions of x
and y
within main
require the types Node<lazy, AND>
and Node<nonlazy, OR>
to be complete, the class template must be instantiated for these two specializations.由于main
中x
和y
的定义要求类型Node<lazy, AND>
和Node<nonlazy, OR>
是完整的,因此必须为这两个特化实例化类模板。 And instantiating the class template means instantiating the function template declarations, so in each case the program is ill-formed since that declaration contains a non-dependent invalid type.实例化类模板意味着实例化函数模板声明,因此在每种情况下程序都是格式错误的,因为该声明包含非依赖的无效类型。
Side note: As you mentioned, if constexpr
is probably the simplest way to solve this sort of issue in C++17.旁注:正如您所提到的, if constexpr
可能是在 C++17 中解决此类问题的最简单方法。 (The public function could just call one of two private functions with different names, if you desire to have the implementations separate.) But C++20 will introduce "constraints" for templates and functions which will provide an even nicer solution: (如果您希望将实现分开,公共函数可以只调用具有不同名称的两个私有函数之一。)但是 C++20 将为模板和函数引入“约束”,这将提供更好的解决方案:
template<laziness LA, logic LO>
struct Node{
void printLaziness () const requires (LA==lazy) {
std::cout << "lazy" << std::endl;
}
void printLaziness () const requires (LA==nonlazy) {
std::cout << "non-lazy" << std::endl;
}
};
Unlike SFINAE tricks, it's entirely valid to have a member function constrained by an expression equivalent to false
or which otherwise can never be satisfied.与 SFINAE 技巧不同,让成员函数受等价于false
或永远无法满足的表达式约束是完全有效的。 This means it will never be considered a viable candidate for overload resolution.这意味着它永远不会被视为重载决议的可行候选者。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.