如何使用 enable_if 重载

how to use enable_if with overloads

enum class enabler{};

template<typename T> 
class X {
   template<typename std::enable_if<std::is_class<T>::value,enabler>::type = enabler()>
    void func();
    void func(int a);
    void func(std::string b);
};

我有这个 class 和 func 的这 3 个重载。我需要 second/third 版本可用于两种 class/non-class 类型,而第一个版本仅可用于 class 类型。当我尝试如上所述使用 enable_if 时,非 class 类型的 class 实例化给出了编译错误。

我不太确定你在这里 enabler 的目的是什么,但你不能做你正在尝试的事情,因为你的成员函数的声明必须有效,因为 T 不是由 func 推导出来的。要通过添加额外的重载来实现您想要的效果,您可以使用一些适度设计的继承。

struct XBaseImpl {
  // whatever you want in both versions
  void func(int a) { }
  void func(std::string b) { }
};

template <typename, bool> struct XBase;

// is_class is true, contains the extra overload you want
template <typename T>
struct XBase<T, true> : XBaseImpl {
  static_assert(std::is_class<T>{}, "");  // just to be safe
  using XBaseImpl::func;
  void func() { }  // class-only
};

// is_class is false
template <typename T>
struct XBase<T, false> : XBaseImpl { };

template<typename T>
class X : public XBase<T, std::is_class<T>{}> { };

要使 SFINAE 起作用,必须推导模板参数。在您的情况下,在您尝试实例化 func 时,T 已经已知,因此如果 enable_if 条件是 false,而不是 SFINAE,则会出现硬错误.

要修复错误,只需添加默认值为 T 的模板参数,并在 enable_if 检查中使用此新参数。现在扣除发生,SFINAE 可以开始非 class 类型。

template<typename U = T,
         typename std::enable_if<std::is_class<U>::value,enabler>::type = enabler()>
void func();

而且您也不需要专用的 enabler 类型,这也可以

template<typename U = T,
         typename std::enable_if<std::is_class<U>::value, int>::type* = nullptr>
void func();

您没有启用或禁用某些东西。
您只需要在一种特定情况下出现编译时错误。
因此,您不需要依赖 sfinae,static_assert 就足够了。

作为一个最小的工作示例:

#include<string>

template<typename T> 
class X {
public:
    void func() {
        static_assert(std::is_class<T>::value, "!");
        // do whatever you want here
    }

    void func(int a) {}
    void func(std::string b) {}
};

int main() {
    X<int> x1;
    X<std::string> x2;

    x2.func(42);
    x2.func();

    x1.func(42);
    // compilation error
    // x1.func();
}

一旦一个 SO 用户说我:这不是 sfinae,这是 - 替换失败总是一个错误 - 在这种情况下你应该使用 static_assert 而不是 .
他是对的,如上例所示,static_assert 比 sfinae 更容易编写和理解,并且也能正常工作。