SFINAE 检查 std::less 是否有效
SFINAE to check if std::less will work
在我的代码中,如果一个对象小于另一个对象,我希望有一个操作先于另一个操作发生。但是,如果类型不具有可比性,则顺序无关紧要。为了实现这一点,我尝试使用SFINAE:
template<typename T, typename = decltype(std::declval<std::less<T>>()(std::declval<T>(), std::declval<T>()))>
bool ComparableAndLessThan(const T& lhs, const T& rhs) {
return std::less<T>()(lhs, rhs);
}
bool ComparableAndLessThan(...) {
return false;
}
struct foo {};
int main()
{
foo a,b;
if (ComparableAndLessThan(a, b)) {
std::cout << "a first" << std::endl;
} else {
std::cout << "b first" << std::endl;
}
}
然而,这并没有奏效。如果我创建一个对象而不给它一个运算符
error C2678: binary '<': no operator found which takes a left-hand operand of type 'const foo' (or there is no acceptable conversion)
note: could be 'bool std::operator <(const std::error_condition &,const std::error_condition &) noexcept'
note: or 'bool std::operator <(const std::error_code &,const std::error_code &) noexcept'
note: while trying to match the argument list '(const foo, const foo)'
note: while compiling class template member function 'bool std::less<T>::operator ()(const _Ty &,const _Ty &) const'
with
[
T=foo,
_Ty=foo
]
我假设因为声明存在,SFINAE 不会将此视为错误,即使实现导致错误。有没有办法检查std::less是否可以在模板化类型上使用?
代码有两个问题。首先很容易修复:默认模板参数不是重载解析的一部分,不应用于 SFINAE 类型的解析。有一个规范的修复,你创建一个类型为your_decltype
* 的非类型模板参数并将其默认为nullptr
.
第二个问题更难。即使使用上述修复,SFINAE 也无法正常工作,因为没有替代错误。std::less<T>
是为每个T
定义的,只是调用operator<
时存在编译错误。解决该问题的一种方法是直接将operator<
用于您的类型:
template<typename T,
decltype(std::declval<T>() < std::declval<T>())* = nullptr>
bool ComparableAndLessThan(const T& lhs, const T& rhs) {
return std::less<T>()(lhs, rhs);
}
bool ComparableAndLessThan(...) {
return false;
}
但它可能不是你想要的。我不知道如何让它与定义非常广泛的std::less
一起工作.
我想通过一个简单的例子来说明问题所在。
首先,SergeyA是正确的,您需要将其设置为默认nullptr
的非类型模板参数。
现在谈谈为什么它不起作用。
因为operator()
std::less
对SFINAE不友好。让我告诉你我的意思:
template <class T> struct Not_working
{
// not SFINAE friendly
auto operator()(T a, T b) { return a < b; }
};
这就是std::less::operator()
的样子。方法operator()
是无条件定义的。即使T
有没有<
,operator()
也存在并具有有效的声明。错误出在函数的主体中。
当您在 SFINAE 上下文中使用Not_working::operator()
时,它的主体不是直接上下文,因此就 SFINAE 而言,Not_working::operator()
是有效的,而不是失败的。因此,为重载集保留了专用化,然后我们有一个错误。
为了在SFINAE中使用,如果身体有错误,operator()
不得参与过载解决。换句话说,它本身必须是SFINAEd:
template <class T> struct Working
{
// SFINAE friendly
template <class TT = T, decltype(TT{} < TT{})* = nullptr>
auto operator()(T a, T b) { return a < b; }
};
现在,当Working::operator()
像在您的ComparableAndLessThan
一样在 SFINAE 上下文中使用时,我们将出现不是错误的替换失败,因此 SFINAE 将按预期工作。
以我的拙见,不让这些成员对SFINAE友好是委员会的疏忽,但可能有一些因素我没有考虑。
你可以这样重写它。
template<typename T, typename F = typename std::enable_if<std::is_same<decltype(std::declval<T>() < std::declval<T>()), bool>::value, bool>::type>
F ComparableAndLessThan(const T& lhs, const T& rhs) {
return std::less<T>()(lhs, rhs);
}
bool ComparableAndLessThan(...) {
return false;
}
更改了您的支票,改为查看decltype(std::declval<T>() < std::declval<T>())
是否产生布尔值。
SFINAE 仅在模板的直接上下文中工作。在您的情况下,错误发生在 std::less::operator() 中 但是,您可以首先测试操作员
- 如果变量名称不跟在 char* 后面,const char* 是否有效?
- 钳制迭代器是否有效
- 检查由括号、方括号和大括号组成的一组方括号是否有效?
- 在函数内创建的对象的范围 - 如果在函数外部存储和访问引用,它们是否有效?
- 模板签名解析为 void(void) 被 GCC 拒绝;这是否有效C++?
- 我如何知道作为参数的size_t在函数中是否有效?
- 我的运算符重载是否有效<<(流插入)左操作数不是 ostream
- C++ 返回指向函数内定义的静态数组的指针是否有效?
- 此递归模板类型定义是否有效C++?
- 将 C 函数转换为 C++ 以检查数字是否有效
- 函数参数的名称与调用函数时使用的变量相同是否有效?
- 如何检查输入是否有效?
- 如何检查用户的输入是否有效以及我正在寻找的数字?
- 在函数中按值传递 unordered_map/unordered_set 是否有效? C++
- 如何检查isupper(cstr)是否有效?
- 将 std::transform 与 std::back_inserter 一起使用是否有效?
- 只需要知道我在c ++中打印模式的方式是否有效,或者有另一种方法可以有效地做到这一点
- 如何检查字符串格式在读取C++文本文件时是否有效?
- 在 c++ 中将对象设置为等于同一类的构造函数是否有效?
- 创建加密安全密码.并验证它是否有效