使用 decltype/SFINAE 检测运算符支持
一篇(有些)过时的文章探讨了使用decltype
与 SFINAE 一起检测类型是否支持某些运算符,例如 ==
或 <
。
下面是检测类是否支持 <
运算符的示例代码:
template <class T>
struct supports_less_than
{
static auto less_than_test(const T* t) -> decltype(*t < *t, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
输出 true
,因为 std::string
当然支持 ><
运算符。但是,如果我尝试将它与不支持 <
运算符的类一起使用,则会收到编译器错误:
error: no match for ‘operator<’ in ‘* t < * t’
因此 SFINAE 在此不起作用。我在 GCC 4.4 和 GCC 4.6 上尝试过,两者都表现出相同的行为。那么,是否可以通过这种方式使用SFINAE来检测类型是否支持某些表达式呢?
A (somewhat) outdated article explores ways to use decltype
along with SFINAE to detect if a type supports certain operators, such as ==
or <
.
Here's example code to detect if a class supports the <
operator:
template <class T>
struct supports_less_than
{
static auto less_than_test(const T* t) -> decltype(*t < *t, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
This outputs true
, since of course std::string
supports the <
operator. However, if I try to use it with a class that doesn't support the <
operator, I get a compiler error:
error: no match for ‘operator<’ in ‘* t < * t’
So SFINAE is not working here. I tried this on GCC 4.4 and GCC 4.6, and both exhibited the same behavior. So, is it possible to use SFINAE in this manner to detect whether a type supports certain expressions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在 C++11 中,我发现的最短最通用的解决方案是:
与
g++ 4.8.1
和clang++ 3.3
配合使用针对任意运算符的更通用的解决方案(更新 2014)
有一个更通用的解决方案,它利用了这样一个事实:所有内置运算符都可以通过 STD 运算符包装器访问(并且可能是专门化的),例如
std::less
(二进制)或std::negate
(一元)。这可以以非常通用的方式使用,特别是在 C++14 中,其中类型推导被延迟到运算符包装器调用(“透明运算符”)。
对于二元运算符,它可以用作:
对于一元运算符:(
使用 C++11 标准库有点复杂,因为实例化
decltype(std::less()(. ..))
即使没有为random_type
定义操作,也可以在 C++11 中实现手动透明运算符,这是 C++14 中的标准)语法非常简单光滑的。我希望标准中采用这样的内容。
两个扩展:
1)它可以检测原始函数应用程序:
2)它还可以检测结果是否可转换/可与某种类型进行比较,在本例中为 double
double <支持 double
,但会返回编译时 false,因为结果不是指定的结果。注意:我只是尝试在 http://melpon.org 中使用 C++14 编译代码/wandbox/ 但它不起作用。我认为该实现(clang++ 3.5 c++14)中的透明运算符(例如std::less<>
)存在问题,因为当我实现自己的less<> 时;
自动扣除效果很好。In C++11 the shortest most general solution I found was this one:
Works with
g++ 4.8.1
andclang++ 3.3
A more general solution for arbitrary operators (UPDATE 2014)
There is a more general solution that exploits the fact that all built-in operators are also accessible (and posibly specialized) through STD operator wrappers, such as
std::less
(binary) orstd::negate
(unary).This can be used in a quite general way, especially in C++14, where type deduction is delayed to the operator wrapper call ("transparent operators").
For binary operators it can be used as:
For unary operators:
(With the C++11 standard library is a bit more complicated because there is no failure on instatiating
decltype(std::less<random_type>()(...))
even if there is no operation defined forrandom_type
, one can implement manually transparent operators in C++11, that are standard in C++14)The syntax is quite smooth. I hope something like this is adopted in the standard.
Two extensions:
1) It works to detect raw-function applications:
2) It can additionally detect if the result is convertible/comparable to a certain type, in this case
double < double
is supported but a compile-time false will be returned because the result is not the specified one.Note: I just tried to compile the code with C++14 in http://melpon.org/wandbox/ and it didn't work. I think there is a problem with transparent operators (likestd::less<>
) in that implementation (clang++ 3.5 c++14), since when I implement my ownless<>
with automatic deduction it works well.您需要将 less_than_test 函数设为模板,因为 SFINAE 代表替换失败不是错误,并且代码中没有可能导致选择失败的模板函数。
You need to make your less_than_test function a template, since SFINAE stands for Substitution Failure Is Not An Error and there's no template function that can fail selection in your code.
这是 C++0x,我们不再需要基于
sizeof
的技巧...;-](这是基于 iammilind 的答案,但不需要
T< /code> 的
operator<
返回类型的大小与long long
不同,并且不要求T
可以默认构造。 )This is C++0x, we don't need
sizeof
-based tricks any more... ;-](This is based on iammilind's answer, but doesn't require that
T
'soperator<
return-type be a different size thanlong long
and doesn't require thatT
be default-constructable.)下面的简单代码满足您的要求(如果您不想编译错误):
用法:
[注意:如果您希望没有
operator <
的类编译错误,那么很容易用很少的代码生成代码行。]Below simple code satisfies your requirement (if you don't want compile error):
Usage:
[Note: If you want compile error for classes not having
operator <
than it's very easy to generate with very few lines of code.]@xDD确实是正确的,尽管他的例子有点错误。
这在 ideone 上编译:
并产生:
请参阅此处实际操作。
重点是 SFINAE 仅适用于模板函数。
@xDD is indeed correct, though his example is slightly erroneous.
This compiles on ideone:
And results in:
See it here in action.
The point is that SFINAE only applies to template functions.