如何使用 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 更容易编写和理解,并且也能正常工作。
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 更容易编写和理解,并且也能正常工作。