使用decltype/SFINAE检测操作员支持
Detect operator support with 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来检测类型是否支持某些表达式呢?
在c++ 11中,我找到的最短最通用的解是这样的:
#include <type_traits>
template<class T, class = decltype(std::declval<T>() < std::declval<T>() )>
std::true_type supports_less_than_test(const T&);
std::false_type supports_less_than_test(...);
template<class T> using supports_less_than = decltype(supports_less_than_test(std::declval<T>()));
#include<iostream>
struct random_type{};
int main(){
std::cout << supports_less_than<double>::value << std::endl; // prints '1'
std::cout << supports_less_than<int>::value << std::endl; // prints '1'
std::cout << supports_less_than<random_type>::value << std::endl; // prints '0'
}
适用于g++ 4.8.1
和clang++ 3.3
任意运算符的更通用的解决方案(UPDATE 2014)
有一个更通用的解决方案,它利用了所有内置操作符也可以通过STD操作符包装访问(并且可能是专一化的)这一事实,例如std::less
(二进制)或std::negate
(一元)。
template<class F, class... T, typename = decltype(std::declval<F>()(std::declval<T>()...))>
std::true_type supports_test(const F&, const T&...);
std::false_type supports_test(...);
template<class> struct supports;
template<class F, class... T> struct supports<F(T...)>
: decltype(supports_test(std::declval<F>(), std::declval<T>()...)){};
这可以以非常通用的方式使用,特别是在c++ 14中,类型推导延迟到操作符包装器调用("透明操作符")。
对于二进制操作符,它可以用作:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::less<>(double, double)>::value << std::endl; // '1'
std::cout << supports<std::less<>(int, int)>::value << std::endl; // '1'
std::cout << supports<std::less<>(random_type, random_type)>::value << std::endl; // '0'
}
对于一元操作符:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::negate<>(double)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(int)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(random_type)>::value << std::endl; // '0'
}
(c++ 11标准库有点复杂,因为即使没有为random_type
定义操作,也不会在decltype(std::less<random_type>()(...))
的安装上失败,可以在c++ 11中实现手动透明操作符,这是c++ 14的标准)
语法非常流畅。我希望在标准中采用这样的内容。
两个扩展
:
1)它可以检测原始功能应用程序:
struct random_type{};
random_type fun(random_type x){return x;}
int main(){
std::cout << supports<decltype(&fun)(double)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(int)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(random_type)>::value << std::endl; // '1'
}
2)它还可以检测结果是否可转换/可比较于某种类型,在这种情况下,double < double
是支持的,但编译时将返回false,因为结果不是指定的。
std::cout << supports<std::equal_to<>(std::result_of<std::less<>(double, double)>::type, random_type)>::value << std::endl; // '0'
注意:我只是试图用c++ 14编译http://melpon.org/wandbox/中的代码,它没有工作。我认为在该实现(clang++ 3.5 c++14)中透明操作符(如std::less<>
)存在问题,因为当我实现自己的less<>
与自动扣除时,它工作得很好。
你需要让你的less_than_test函数成为一个模板,因为SFINAE代表替代失败不是一个错误,并且没有模板函数可以在你的代码中失败选择。
template <class T>
struct supports_less_than
{
template <class U>
static auto less_than_test(const U* u) -> decltype(*u < *u, 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;
}
这是c++ 0x,我们不再需要基于sizeof
的技巧了…; -)
#include <type_traits>
#include <utility>
namespace supports
{
namespace details
{
struct return_t { };
}
template<typename T>
details::return_t operator <(T const&, T const&);
template<typename T>
struct less_than : std::integral_constant<
bool,
!std::is_same<
decltype(std::declval<T const&>() < std::declval<T const&>()),
details::return_t
>::value
> { };
}
(这是基于iammilind的答案,但不要求T
的operator<
返回类型与long long
的大小不同,也不要求T
是默认可构造的)
下面的简单代码满足您的要求(如果您不想编译错误):
namespace supports {
template<typename T> // used if T doesn't have "operator <" associated
const long long operator < (const T&, const T&);
template <class T>
struct less_than {
T t;
static const bool value = (sizeof(t < t) != sizeof(long long));
};
}
用法:
supports::less_than<std::string>::value ====> true; // ok
supports::less_than<Other>::value ====> false; // ok: no error
operator <
比它很容易生成很少的代码行]
@xDD确实是正确的,尽管他的例子有点错误。
在ideone上编译:
#include <array>
#include <iostream>
struct Support {}; bool operator<(Support,Support) { return false; }
struct DoesNotSupport{};
template <class T>
struct supports_less_than
{
template <typename U>
static auto less_than_test(const U* u) -> decltype(*u < *u, 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<Support>::value << std::endl;
std::cout << std::boolalpha <<
supports_less_than<DoesNotSupport>::value << std::endl;
}
,结果是:
true
false
看它在这里的行动。
关键是SFINAE只适用于模板函数。- 编译时未启用intel oneApi CUDA支持
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用CMake检测支持的C++标准
- 为什么istream不支持右值提取
- 当我编译webrtc服务器时,Windows上只支持clang-cl
- 扩展光电二极管探测器以支持多个传感器
- MSVC是否支持C++11样式的属性而不是__declspec
- <<操作员在下面的行中工作
- 在使用GPU支持编译Tensorflow时,会遇到CUDA_TOOLKIT_PATH未绑定变量
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- C++ 与操作员不匹配<<
- 操作员C++的模棱两可的过载
- C++中>>操作员过载时出现问题?
- NaN 上的宇宙飞船操作员
- 错误:(-210:不支持的格式或格式组合)功能'create'中的硬件视频解码器不支持视频源
- 哪个C++规范开始支持 std::vector
- 如何检测VS C++编译器是否支持C++11?
- 操作员支持的数字范围
- 推力:不支持操作员'*'
- 使用decltype/SFINAE检测操作员支持