修改 SFINAE 习语以使用 std::is_arithmetic 检查 return 函数类型

Modify SFINAE idiom to check return type of a function using std::is_arithmetic

我正在使用 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);
};

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

我想这就是您要找的:

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。如果 class 有一个带有 A_... 参数和算术 return 类型的方法,则 value 为真。否则为假。

除了@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,虽然我已经用 uint8_t 替换了 size_t 以证明如果所需类型可以隐式转换为查询类型的成员函数中的参数类型, 那么表达式的计算结果为真。

编辑:我已经更新了答案以允许给定方法采用任意参数,而不仅仅是具有 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 概念来确保在 T 上访问 some_method 是格式正确的,然后从 std::is_arithmetic 派生如果格式正确。我不得不添加一个助手来实现 return 类型的任意函数。


一些要测试的结构。注意那些应该通过的参数如何接受 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 的所有工作留给了继承技巧。