使用SFINAE切换构造函数时遇到问题

时间:2019-05-04 00:43:11

标签: c++ sfinae

我想根据模板参数yes

选择两个构造函数。
template <bool yes>
class Base {
public:
  template<typename std::enable_if< yes, int>::type = 0>
  Base() { /* yes */ }

  template<typename std::enable_if<!yes, int>::type = 0>
  Base() { /* no */ }
};

我很困惑为什么会产生编译器错误

failed requirement '!true'; 'enable_if' cannot be used to disable this declaration

Base<true>

no type named 'type' in 'std::__1::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration

Base<false>上。我找不到其他任何变体,包括thisthisthis也不起作用。如何基于yes选择要使用的构造函数?

1 个答案:

答案 0 :(得分:1)

这里有几个问题。第一件事是默认模板模板参数的语法错误,应该是:

template <bool yes>
class Base {
public:
  template<typename T=std::enable_if< yes, int>::type>
  Base() { /* yes */ }

  template<typename T=std::enable_if<!yes, int>::type>
  Base() { /* no */ }
};

但这也不起作用,因为默认参数值不是模板签名的一部分,因此,大致来说,这等效于:

  template<typename T>
  Base() { /* yes */ }

  template<typename T>
  Base() { /* no */ }

这就是两个构造函数的签名对编译器的外观。两者都是带有单个参数的模板,因此,出于重载解析的目的,两个构造函数都具有相同的签名,并且比声明两个“ Base(int foo)”构造函数要好得多。如果声明,将得到相同的错误

Base(int foo=0)

Base(int foo=1)

构造函数。两个构造函数都具有相同的签名。默认值不是签名的一部分。

有几种传统的方法可以解决此问题。 C ++库本身的常见设计模式是声明一些辅助空类,并将它们用作附加参数以消除不同方法的歧义,以实现重载解决方案。例如,使用std::in_place_tstd::variant's constructor的等效功能选择std::optionalstd::in_place_type_t的特定重载构造函数。

在您的情况下,我们可以结合委托的构造函数来完全自动使用占位符参数:

#include <iostream>

struct bool_true {};
struct bool_false {};

template<bool value> class bool_value;

template<>
struct bool_value<true> {

    typedef bool_true type;
};

template<>
struct bool_value<false> {

    typedef bool_false type;
};

template<bool v>
using bool_value_t=typename bool_value<v>::type;


template <bool yes>
class Base {
public:

    Base() : Base{ bool_value_t<yes>{} } {}

    Base(const bool_true &)
    {
        std::cout << "Yes" << std::endl;
    }

    Base(const bool_false &)
    {
        std::cout << "No" << std::endl;
    }
};

int main()
{
    Base<true> t;
    Base<false> f;
    return 0;
}