在我的代码中,如果一个对象小于另一个对象,我希望在另一个对象之前执行一个操作。但是,如果类型不具有可比性,则与订单无关。为此,我尝试使用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;
}
}
然而这不起作用。如果我创建一个对象而不给它一个运算符&lt;或者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?
答案 0 :(得分:6)
代码有两个问题。首先很容易修复:默认模板参数不是重载决策的一部分,不应该用于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
。
答案 1 :(得分:3)
我想通过一个简单的例子来说明问题所在。
首先,SergeyA是正确的,您需要将其设为非类型模板参数默认nullptr
。
现在为什么它不起作用。
operator()
的{{1}}不是SFINAE友好的。让我告诉你我的意思:
std::less
这就是template <class T> struct Not_working
{
// not SFINAE friendly
auto operator()(T a, T b) { return a < b; }
};
的样子。方法std::less::operator()
是无条件定义的。即使operator()
有T
,<
仍然存在,并且有效声明。错误发生在函数体内。
当你在SFINAE环境中使用operator()
时,它的主体不是直接上下文所以为了SFINAE的目的,Not_working::operator()
是有效的而不是失败。因此,为重载集保留了专门化,然后我们就出错了。
为了在SFINAE中可用,Not_working::operator()
如果身体有错误,则不得参与重载解析。换句话说,它本身必须是SFINAEd:
operator()
现在,当template <class T> struct Working
{
// SFINAE friendly
template <class TT = T, decltype(TT{} < TT{})* = nullptr>
auto operator()(T a, T b) { return a < b; }
};
在SFINAE上下文中使用Working::operator()
时,我们将会发生替换失败,这不是错误,因此SFINAE将按预期工作。
我谦虚地认为,不让这些成员SFINAE友好是对委员会的监督,但可能有一些我不考虑的因素。
答案 2 :(得分:0)
你可以像这样重写它。
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>())
是否会产生布尔值。
答案 3 :(得分:-1)
SFINAE仅适用于模板的直接上下文。在您的情况下,错误发生在std :: less :: operator()中 但是,您可以首先测试运算符&lt;可用;如果没有,请检查std :: less的特化是否可用。看看this。