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

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

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

检查类型是否C已有成员(member)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_member 的基本模板时和has_function (仅包含 static_assert ), has_member始终评估为 false 且 has_function不再编译并提示 free_func不接受 0 个参数。 我假设模板参数未正确绑定(bind)到 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的那些。自 free_func不能用两个 double 调用s,该替换失败 - 但它不在替换这些本地参数的直接上下文中,因此这是一个硬错误。您需要更改它以确保您要替换的内容与您要检查的表达式相关。即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 定义时的范围内.

关于c++ - 检查成员函数、自由函数和运算符是否存在的统一方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49862751/

相关文章:

c++ - sizeof(* struct pointer) 是否给你结构的值

c++ - 在多参数情况下使用转换构造函数

c++ - 以 pair 为键的 map (vs) unordered_map

c++ - 实现 `to_xstring()` 来合并 `to_string()` 和 `to_wstring()`

c++ - 如何概括具有变体/访问者的树结构

c++ - AActor在ue4中设置移动性

c++ - 可变参数模板的性能影响

C++:复制构造函数正在删除原始对象中的节点

c++ - 如何声明模板类的模板

c++ - 从具有相同容器类型但不同 value_type 的容器生成新类型