c++ - 使用 decltype/SFINAE 检测运算符(operator)支持

标签 c++ c++11 sfinae decltype

一个(有点)过时的article探索使用方法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支持<运算符(operator)。但是,如果我尝试将它与 支持 < 的类一起使用运算符,我得到一个编译器错误:

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.1clang++ 3.3


任意运算符的更通用解决方案(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标准库稍微复杂一点,因为初始化decltype(std::less<random_type>()(...))没有失败,即使没有为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'

注意:我只是尝试在 http://melpon.org/wandbox/ 中使用 C++14 编译代码它没有用。我认为该实现(clang++ 3.5 c++14)中的透明运算符(如 std::less<> )存在问题,因为当我实现自己的 less<> 时自动扣款效果很好。

关于c++ - 使用 decltype/SFINAE 检测运算符(operator)支持,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5839357/

相关文章:

c++ - 实例化同一类中的多个对象时,代码不会运行,但是实例化具有相同代码的重复类中的对象时,代码会起作用

java - 如何在 Java 中使用 & 运算符?移植C代码

c++ - STL 顺序容器支持 insert (before) 而 forward_list 支持 insert_after

c++11 - g++ 4.4.3 中的 std::atomic 支持

c++ - SFINAE 意想不到的结果

c++ - SFINAE 模板错误

c++ - 为什么 Member Detector fallback 必须是 int?

c++ - 一个矩阵中的数据自动复制到 C++ 中 opencv 中的另一个矩阵

c++ - C++ 11中<chrono>头文件的几个问题

c++ - 前向声明的替代方案 : two classes using each other