修改SFINAE惯用法以使用std :: is_arithmetic检查函数的返回类型

时间:2015-12-02 16:07:02

标签: c++ templates template-meta-programming sfinae

我正在使用SFINAE惯用法来检查某个类型是否有定义了给定签名的方法(some_method()):

template <typename... Other>
struct has_method {
    static constexpr bool value = has_method<Other...>::value;
};

template <typename U, typename... Other>
struct has_method<U, Other...> {
    static constexpr bool value =
        has_method<U>::value && has_method<Other...>::value;
};

template <typename U>
struct has_method<U> {
    template <typename T, T>
    struct helper;
    template <typename T>
    static std::uint8_t check(helper<int (*)(size_t), &T::some_method>*);
    template <typename T>
    static std::uint16_t check(...);

    static constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};

我想修改它以使用std::is_arithmetic检查函数的返回类型是否为算术类型。这可能吗? 我尝试使用static_assert()std::is_arithmetic< decltype(((T*)nullptr)->foo())>::value一起修改它,但没有成功。

3 个答案:

答案 0 :(得分:1)

我认为这就是你要找的东西:

struct Unknown {};

template <typename T_>
struct Wrap
{
    typedef T_ type;
};

template <class C_, typename... A_>
struct HasMethodArithmeticReturn
{
    template <class U_>
    static Wrap<decltype(std::declval<U_>().NAME(std::declval<A_>()...))> test(void*);
    template <class U_>
    static Unknown test(...);

    typedef char yes[1];
    typedef char no[2];

    template <typename>
    struct helper
    {
        typedef no type;
    };
    template <typename R_>
    struct helper<Wrap<R_>>
    {
        template <class U_, R_(U_::*)(A_...)>
        struct assist
        {
            typedef yes type;
        };
        template <class U_>
        static typename std::enable_if<std::is_arithmetic<R_>::value, typename assist<U_, &U_::NAME>::type>::type& test(void*);
        template <class U_>
        static no& test(...);
        typedef decltype(test<C_>(0)) type;
    };
    static const bool value = sizeof(yes) == sizeof(typename helper<decltype(test<C_>(0))>::type);
};

您可以看到它使用live。如果类具有带A_...参数和算术返回类型的方法,则value为真。否则,这是错误的。

答案 1 :(得分:1)

除了@James Root的答案,它允许用户指定参数,但不允许用户在OP中同时测试多个类,您还可以执行以下操作:

#include <type_traits>

template <typename... Other>
struct has_method {
    static constexpr bool value = has_method<Other...>::value;
};

template <typename U, typename... Other>
struct has_method<U, Other...> {
    static constexpr bool value =
        has_method<U>::value && has_method<Other...>::value;
};

template <typename U>
struct has_method<U> {
    template <typename T>
    static auto typeget(int) -> decltype(T::some_method(size_t{}));
    template <typename T>
    static void typeget(...);


    static constexpr bool value = 
        std::is_arithmetic<decltype(has_method::typeget<U>(0))>::value;
};

实现语法更简单,但您需要以其他方式付费:您必须为希望提供给函数的每组参数类型创建一个新模板,并检测是否可以使用参数,而不是函数是否具有确切的期望参数。

Live on Coliru,虽然我已将size_t替换为uint8_t,以证明所需类型可以隐式转换为查询类型的成员函数中的参数类型,然后表达式评估为真。

答案 2 :(得分:1)

编辑:我已更新答案以允许给定方法采取任意参数,而不仅仅是void签名

我认为这会解决问题:

namespace details {
  template<typename...>
  struct voider
  {
      using type=void;
  };

  template<typename T>
  struct GetReturnType;

  // non member function
  template<typename Ret, typename... Args>
  struct GetReturnType<Ret(*)(Args...)>
  {
      using type = Ret;
  };

  // mmeber function
  template<typename Ret, typename C, typename... Args>
  struct GetReturnType<Ret(C::*)(Args...)>
  {
      using type = Ret;
  };
}

template<typename...Ts>
using void_t = typename details::voider<Ts...>::type;

template<typename T, typename=void>
struct has_arithmetic_method : std::false_type{};

template<typename T>
struct has_arithmetic_method<T, void_t<decltype(&T::some_method)>> :  std::is_arithmetic<typename details::GetReturnType<decltype(&T::some_method)>::type>::type{};

我使用了voider概念来确保some_method上的T访问格式正确,然后从std::is_arithmetic派生,如果是的话。我必须添加一个帮助器来拉出任意函数的返回类型。

一些结构要进行测试。请注意应该传递的内容如何为some_method提供不同的参数:

struct FailSomeMethod // lacks a some_method altogether
{
    void some_other_method()
    {
        std::cout << "FailSomeMethod::some_other_method" << std::endl;
    }
};

struct PassSomeMethod // has a some_method, but it's not arithmetic
{
    void some_method()
    {
        std::cout << "PassSomeMethod::some_method" << std::endl;
    }
};

struct PassArithmeticMethod 
{
    int some_method(int _a)
    {
        std::cout << "PassArithmeticMethod::some_method" << std::endl;
        return 1;
    }
};

struct PassArithmeticMethod2
{
    double some_method()
    {
        return 1.0;
    }
};

struct PassArithmeticMethod3
{
    float some_method(int a, double b, float c, char d, bool e)
    {
        return 1.;
    }
};

实际测试:

int main()
{
    //test the has_arithmetic_method concept
    std::cout << "Struct PassArithmeticMethod: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod>::value << std::endl;
    std::cout << "Struct PassSomeMethod: " << std::boolalpha << has_arithmetic_method<PassSomeMethod>::value << std::endl;
    std::cout << "Struct FailSomeMethod: " << std::boolalpha << has_arithmetic_method<FailSomeMethod>::value << std::endl;

    std::cout << "Struct PassArithmeticMethod2: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod2>::value << std::endl;
    std::cout << "Struct PassArithmeticMethod3: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod3>::value << std::endl;
}

输出:

  

Struct PassArithmeticMethod:true
  Struct PassSomeMethod:false
  Struct FailSomeMethod:false
  Struct PassArithmeticMethod2:true
  Struct PassArithmeticMethod3:true

Live Demo

老实说,尽管我的回答(误导性)很长,但我觉得这是最简单的。 voider概念和GetReturnType助手是通用结构,可以很容易地重复使用。我的代码中没有使用任何constexpr;我将所有工作留给了truefalse继承技巧。