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

时间:2018-04-16 17:06:35

标签: c++ templates c++14 enable-if

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

检查类型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 编译条件刚刚从返回类型移动到模板参数。

检查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,但我并不完全理解它。  所以这里的任何解释也将受到赞赏。

1 个答案:

答案 0 :(得分:4)

  

我现在的问题是:在尝试检查具有给定名称和签名的自由函数是否存在时,我做错了什么?

首先 - 你不是在这里检查签名。您正在检查给定特定参数列表的调用表达式是否产生特定结果:

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的参数。由于free_func不能用两个double调用,因此该替换失败 - 但它不是在替换那些本地参数的直接上下文中,所以这是一个很难的错误。您需要更改它以确保您替换的内容与您正在检查的表达式相关。也就是说,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;
};

另请注意,为了使此类与非类类型一起用作参数,free_func必须在has_function的定义范围内。