SFINAE 检查 std::less 是否有效

SFINAE to check if std::less will work

本文关键字:是否 有效 less SFINAE std 检查      更新时间:2023-10-16

在我的代码中,如果一个对象小于另一个对象,我希望有一个操作先于另一个操作发生。但是,如果类型不具有可比性,则顺序无关紧要。为了实现这一点,我尝试使用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() 中 但是,您可以首先测试操作员