在模板成员函数的返回类型中使用std :: enable_if时的编译器差异

时间:2018-09-23 16:12:34

标签: c++ templates gcc enable-if

我尝试为struct Node中的方法定义两个“重载”,这些重载取决于结构模板参数调用不同的行为。

我知道我可以使用其他方法(例如if constexpr或其他任何方法)。

我担心的是,我“诱使” gcc 9.0.0实验性20180919对此进行了编译,但wandbox(以下永久链接)中提供的所有其他最近的编译器都没有。我想gcc8和clang6 / 7在这里做正确的事?

感谢您的帮助!

#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

2 个答案:

答案 0 :(得分:1)

代码格式错误,并且实验性gcc 9.0.0忽略了它是错误的。

要理解SFINAE规则的关键是,当形成无效类型或表达式时,将功能模板的参数替换为其他功能模板参数的默认模板参数或函数类型时,该规则将适用,如[temp.deduct]节的介绍;或确定类模板的部分专业化是否匹配或最专业的部分专业化时。但是在您的代码中,无效类型是在替换类模板的参数LA而不是函数模板的参数时形成的。

[temp.inst]/2段也与此相关:

  

类模板专业化的隐式实例化导致

     
      
  • 未删除的类成员函数,成员类,作用域成员枚举,静态数据成员,成员模板和好友的声明的隐式实例化,而不是定义的隐式实例化;和

  •   
  • 已删除成员函数,无作用域成员枚举和成员匿名联合的定义的隐式实例化。

  •   
     

类模板专门化的隐式实例化不会导致默认参数或类成员函数的 noexcept-specifier 的隐式实例化。

由于xymain的定义要求类型Node<lazy, AND>Node<nonlazy, OR>完整,因此必须实例化这些类的模板两个专业。实例化类模板意味着实例化功能模板声明,因此每种情况下程序都是错误的,因为该声明包含一个非依赖的无效类型。


旁注:如您所述,if constexpr可能是解决C ++ 17中此类问题的最简单方法。 (如果您希望将实现分开,则公共函数可以只使用两个不同名称的私有函数之一。)但是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;
    }
};

与SFINAE技巧不同,使成员函数受到与false等价的表达式约束或完全无法满足的表达式是完全有效的。这意味着它将永远不会被认为是解决过载的可行方法。

答案 1 :(得分:0)

我认为这一定是一个错误(因此是GCC 9.0版本中的一个错误)的原因是enable_if不在SFINAE- fashion 中评估,因为没什么可推论或替代的,因为其模板参数在struct template实例化时是已知的。

因此,将同时生成false_type'd版本和true_type'd版本,这将导致false_type'd版本的错误。

要想让SFINAE参与进来,就必须在评估时做出一些推断。

关于模板功能,可以将其简化为:

void printLaziness () const 
{
      std::cout << ((LA==lazy) ? "lazy" : "non-lazy") << std::endl;
}

任何优化程序都应将其进一步缩小为

//当实例化

void printLaziness () const 
{
    std::cout << "lazy" << std::endl;
}

//当实例化

void printLaziness () const 
{
    std::cout << "non-lazy" << std::endl;
}
相关问题