检查成员函数、自由函数和运算符是否存在的统一方法

Unified way for checking the existence of member functions, free functions and operators

我在这里发现了几个问题,它们涉及检查给定类型是否存在成员函数、自由函数或运算符。 所提出的解决方案解决了手头的问题,但每个都使用不同的方法。 我正试图找到一种方法来以相同或至少相似的方式处理这些问题中的每一个。

检查类型 C 是否有成员 func 有效:

template<typename, typename T>
    struct has_member {
        static_assert(
            std::integral_constant<T, false>::value,
            "Second template parameter needs to be of function type.");
};

template<typename C, typename Ret, typename... Args>
struct has_member<C, Ret(Args...)> {
private:
    template<typename T, std::enable_if_t<
        std::is_same
        <
            decltype(std::declval<T>().func(std::declval<Args>()...)),
            Ret   
        >::value 
    > * = nullptr >
    static constexpr std::true_type check(T*);

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<C>(nullptr)) type;

public:
    static constexpr bool value = type::value;
};

这摘自这个问题的最高投票答案: Check if a class has a member function of a given signature 编译条件刚刚从 return 类型移动到模板参数。

检查operator+是否存在也可以:

template<typename C, typename Ret, typename Arg>
struct has_operator {
private:
    template<typename T, std::enable_if_t<
        std::is_same
        <
            decltype(std::declval<T>() + std::declval<Arg>()),
            Ret
        >::value
    > * = nullptr >
    static constexpr std::true_type check(T*);

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<C>(nullptr)) type;

public:
    static constexpr bool value = type::value;
};

正在检查,如果存在自由函数 free_func 但不起作用:

template<typename T>
    struct has_function {
        static_assert(
            std::integral_constant<T, false>::value,
            "Second template parameter needs to be of function type.");
};

template<typename Ret, typename... Args>
struct has_function<Ret(Args...)> {
private:
    template<std::enable_if_t
    <
        std::is_same
        <
            decltype(free_func(std::declval<Args>()...)),
            Ret    
        >::value
    > * = nullptr >
    static constexpr std::true_type check(nullptr_t);

    template<typename = void>
    static constexpr std::false_type check(...);

    typedef decltype(check<>(nullptr)) type;

public:
    static constexpr bool value = type::value;
};

具有以下声明:

struct MyStruct{
    int func(double);
    MyStruct operator+(const MyStruct &);
};

int free_func(double);

我得到这些结果:

std::cout << has_member<MyStruct, int(double)>::value << std::endl; // true
std::cout << has_member<MyStruct, int(double, double)>::value << std::endl; // false

std::cout << has_function<int(double)>::value << std::endl; // true
//std::cout << has_function<int(double, double)>::value << std::endl; // compile error: free_func does not take 2 arguments

std::cout << has_operator<MyStruct, MyStruct, MyStruct>::value << std::endl; // true
std::cout << has_operator<int, int, int>::value << std::endl; // true
std::cout << has_operator<std::vector<int>, std::vector<int>, std::vector<int>>::value << std::endl; // false

我现在的问题是:我在尝试检查是否存在具有给定名称和签名的自由函数时做错了什么? 如果我删除 check(Ret*) 的第一个声明,另一个模板将被实例化并正确评估为 false。 我假设我犯了一些错误,所以 SFINAE 在这里不适用。

我还尝试将另一个模板参数添加到 check,但是没有改变结果。

template<typename T, std::enable_if_t
<
    std::is_same
    <
        decltype(free_func(std::declval<Args>()...)),
        Ret
    >::value
> * = nullptr >
static constexpr std::true_type check(T *);

template<typename>
static constexpr std::false_type check(...);

typedef decltype(check<Ret>(nullptr)) type;

我想继续使用 decltype(declval(...)) 风格,因为它允许编译器确定是否存在任何可调用的东西,我不必关心函数是否按值、按引用或通过 const 引用。

感谢任何帮助。非常感谢您。

我一直想知道的另一件事: 当我删除 has_memberhas_function 的基本模板(仅包含 static_assert)时,has_member 始终计算为 false 并且 has_function 不再编译并且抱怨 free_func 不接受 0 个参数。 我假设在使用函数签名语法时模板参数未正确绑定到 RetArgs,但我并不完全理解它。 所以这里的任何解释也将不胜感激。

My question now is: What am I doing wrong while trying to check if a free function with a given name and signature exists?

首先 - 您没有在此处的任何地方检查签名。您正在检查给定特定参数列表的调用表达式是否产生特定结果:

struct X {
    int func(int);
};
static_assert(has_member<X, int(char)>::value, "!"); // doesn't fire

就是说,检查成员函数和检查自由函数之间存在很大差异,那就是哪些模板参数在替换的直接上下文中。在成员函数的情况下,T 是我们尝试代入此表达式的函数模板参数:

template<typename T, std::enable_if_t<
//       ~~~~~~~~~~
    std::is_same
    <
        decltype(std::declval<T>().func(std::declval<Args>()...)),
//               ~~~~~~~~~~~~~~~~~
        Ret   
    >::value 
> * = nullptr >
static constexpr std::true_type check(T*);

自由函数情况下,没有函数模板参数:

template<std::enable_if_t
<
    std::is_same
    <
        decltype(free_func(std::declval<Args>()...)),
        Ret    
    >::value
> * = nullptr >
static constexpr std::true_type check(nullptr_t);

整个表达式可以在实例化时立即代入。它不 依赖 check() 中的任何参数,只依赖 has_function 中的参数。由于无法使用两个 double 调用 free_func,因此该替换失败 - 但它不在替换这些局部参数的直接上下文中,因此这是一个硬错误。您需要更改它以确保您替换的内容与您正在检查的表达式相关。即,Args...:

template<typename Ret, typename... Args>
struct has_function<Ret(Args...)> {
private:
    template<typename... A, std::enable_if_t
//           ~~~~~~~~~~~~~
    <
        std::is_same
        <
            decltype(free_func(std::declval<A>()...)),
//                             ~~~~~~~~~~~~~~~~~
            Ret    
        >::value
    > * = nullptr >
    static constexpr std::true_type check(nullptr_t);

    template<typename...>
//           ~~~~~~~~~~~
    static constexpr std::false_type check(...);

    typedef decltype(check<Args...>(nullptr)) type;
//                   ~~~~~~~~~~~~~~
public:
    static constexpr bool value = type::value;
};

另请注意,为了使其能够使用非 class 类型作为参数,free_func 必须在 has_function 的定义点范围内。