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

Dev*_*all 2 c++ templates enable-if c++14

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

检查类型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;
};
Run Code Online (Sandbox Code Playgroud)

这取自这个问题的最高投票答案: 检查类是否具有给定签名成员函数编译条件刚刚从返回类型移至模板参数。

检查是否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;
};
Run Code Online (Sandbox Code Playgroud)

检查是否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;
};
Run Code Online (Sandbox Code Playgroud)

带有以下声明:

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

int free_func(double);
Run Code Online (Sandbox Code Playgroud)

我得到这些结果:

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
Run Code Online (Sandbox Code Playgroud)

我现在的问题是:在尝试检查具有给定名称和签名的自由函数是否存在时,我做错了什么?如果我删除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;
Run Code Online (Sandbox Code Playgroud)

我想继续使用该decltype(declval(...))样式,因为它允许编译器确定是否存在任何可调用的东西,而且我不必关心该函数是按值、按引用还是按常量引用获取其参数。

任何帮助表示赞赏。非常感谢您提前。

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

Bar*_*rry 5

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

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

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

也就是说,检查成员函数和检查自由函数之间存在很大差异,即哪些模板参数位于替换的直接上下文中。在成员函数的情况下,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*);
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

整个表达式可以在实例化点立即替换。它不依赖于 中的任何参数check(),只依赖has_function. 由于free_func不能用两个doubles调用,因此替换失败 - 但它不在替换这些本地参数的直接上下文中,所以这是一个硬错误。您需要更改它以确保您要替换的内容与您正在检查的表达式相关。即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;
};
Run Code Online (Sandbox Code Playgroud)

另请注意,为了使其与非类类型作为参数一起使用,free_func必须在has_function.