Detect operator support with decltype/SFINAE

前端 未结 5 1901
夕颜
夕颜 2020-12-05 01:12

A (somewhat) outdated article explores ways to use decltype along with SFINAE to detect if a type supports certain operators, such as == or &

5条回答
  •  臣服心动
    2020-12-05 01:55

    In C++11 the shortest most general solution I found was this one:

    #include 
    
    template() < std::declval() )> 
    std::true_type  supports_less_than_test(const T&);
    std::false_type supports_less_than_test(...);
    
    template using supports_less_than = decltype(supports_less_than_test(std::declval()));
    
    #include
    struct random_type{};
    int main(){
        std::cout << supports_less_than::value << std::endl; // prints '1'
        std::cout << supports_less_than::value << std::endl; // prints '1'
        std::cout << supports_less_than::value << std::endl; // prints '0'
    }
    

    Works with g++ 4.8.1 and clang++ 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) or std::negate (unary).

    template()(std::declval()...))> 
    std::true_type  supports_test(const F&, const T&...);
    std::false_type supports_test(...);
    
    template struct supports;
    template struct supports 
    : decltype(supports_test(std::declval(), std::declval()...)){};
    

    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:

    #include
    struct random_type{};
    int main(){
        std::cout << supports(double, double)>::value << std::endl; // '1'
        std::cout << supports(int, int)>::value << std::endl; // '1'
        std::cout << supports(random_type, random_type)>::value << std::endl; // '0'
    }
    

    For unary operators:

    #include
    struct random_type{};
    int main(){
        std::cout << supports(double)>::value << std::endl; // '1'
        std::cout << supports(int)>::value << std::endl; // '1'
        std::cout << supports(random_type)>::value << std::endl; // '0'
    }
    

    (With the C++11 standard library is a bit more complicated because there is no failure on instatiating decltype(std::less()(...)) even if there is no operation defined for random_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:

    struct random_type{};
    random_type fun(random_type x){return x;}
    int main(){
        std::cout << supports::value << std::endl; // '0'
        std::cout << supports::value << std::endl; // '0'
        std::cout << supports::value << std::endl; // '1'
    }
    

    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.

    std::cout << supports(std::result_of(double, double)>::type, random_type)>::value << std::endl; // '0'
    

    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 (like std::less<>) in that implementation (clang++ 3.5 c++14), since when I implement my own less<> with automatic deduction it works well.

提交回复
热议问题