繁体   English   中英

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;
    }
}

但是这没用。 如果我创建一个对象而不给它一个操作符<或std :: less专门化,我会收到此错误。

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也不起作用,因为没有替代错误。 为每个T 定义std::less<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

现在为什么它不起作用。

因为std::less operator()不是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; }
};

现在当在您的ComparableAndLessThan中的SFINAE上下文中使用Working::operator() ,我们将发生替换失败,这不是错误,因此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>())产生了一个bool。

SFINAE仅适用于模板的直接上下文。 在你的情况下,错误发生在std :: less :: operator()中你可以先测试operator <是否可用; 如果没有,请检查std :: less的特化是否可用。 看看这个

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM