匹配成员函数存在和签名:参数

时间:2014-12-24 06:32:50

标签: c++ c++11 traits sfinae typetraits

阅读相关问题"How to call member function only if object happens to have it?""Is it possible to write a C++ template to check for a function's existence?",我正在实施自己的特质课程。目标非常简单,虽然我无法达到我想要的目的:提供一个traits类,静态地将调用重定向到匹配的类

所以,如果我提供给我的特征的类有例如void open_file()方法,则调用它,否则使用traits函数(一个NOP,但现在是输出)。显然,这是一个SFINAE任务,但是对于这个过程并不太熟悉,我已经按照你的想法进行了思考。

void open_file()一切正常,但在尝试void open_file(int)时,它不匹配并调用NOP函数。这是我的尝试(几乎逐字逐句地提到这两个问题!):

template <class Type>
class my_traits
{
    //! Implements a type for "true"
    typedef struct { char value;    } true_class;
    //! Implements a type for "false"
    typedef struct { char value[2]; } false_class;

    //! This handy macro generates actual SFINAE class members for checking event callbacks
    #define MAKE_MEMBER(X) \
        public: \
            template <class T> \
            static true_class has_##X(decltype(&T::X)); \
        \
            template <class T> \
            static false_class has_##X(...); \
        public: \
            static constexpr bool call_##X = sizeof(has_##X<Type>(0)) == sizeof(true_class);

    MAKE_MEMBER(open_file)

public:

    /* SFINAE foo-has-correct-sig :) */
    template<class A, class Buffer>
    static std::true_type test(void (A::*)(int) const)
    {
        return std::true_type();
    }

    /* SFINAE foo-exists :) */
    template <class A>
    static decltype(test(&A::open_file)) test(decltype(&A::open_file), void *)
    {
        /* foo exists. What about sig? */
        typedef decltype(test(&A::open_file)) return_type;
        return return_type();
    }

    /* SFINAE game over :( */
    template<class A>
    static std::false_type test(...)
    {
        return std::false_type();
    }

    /* This will be either `std::true_type` or `std::false_type` */
    typedef decltype(test<Type>(0, 0)) type;

    static const bool value = type::value; /* Which is it? */

    /*  `eval(T const &,std::true_type)`
     delegates to `T::foo()` when `type` == `std::true_type`
     */
    static void eval(Type const & t, std::true_type)
    {
        t.open_file();
    }
    /* `eval(...)` is a no-op for otherwise unmatched arguments */
    static void eval(...)
    {
        // This output for demo purposes. Delete
        std::cout << "open_file() not called" << std::endl;
    }

    /* `eval(T const & t)` delegates to :-
     - `eval(t,type()` when `type` == `std::true_type`
     - `eval(...)` otherwise
     */
    static void eval(Type const &t)
    {
        eval(t, type());
    }

};


class does_match
{
public:
    void open_file(int i) const { std::cout << "MATCHES!" << std::endl; };
};

class doesnt_match
{
public:
    void open_file() const { std::cout << "DOESN'T!" << std::endl; };
};

如您所见,我已经实现了两者,第一个使用宏MAKE_MEMBER只检查成员是否存在,并且它可以正常工作。接下来,我尝试将它用于静态SFINAE if/else ie ,如果存在成员函数则调用它,否则使用预定义函数,但没有成功(正如我所说,我我不太深入SFINAE)。

第二次尝试几乎是从检查签名和存在问题逐字逐句,但我已修改它以处理参数。但是,它不起作用:

    does_match   it_does;
    doesnt_match it_doesnt;

    my_traits<decltype(it_does)>::eval(it_does);
    my_traits<decltype(it_doesnt)>::eval(it_doesnt);

    // OUTPUT:
    // open_file() not called
    // open_file() not called

显然,这里有问题:我没有提供参数,但我不知道怎么做。

我正在尝试理解和学习,我是否可以使用取决于模板参数的open_file(),例如SFINAE匹配template <class T> open_file(T t)

谢谢&amp;干杯!

1 个答案:

答案 0 :(得分:3)

问题在于您致电test(&A::open_file)

typedef decltype(test(&A::open_file)) return_type;

总是匹配:

static std::false_type test(...)

因为您的 true-test 有一个未受影响的类型模板参数Buffer

template<class A, class Buffer>
//                      ~~~~~^
static std::true_type test(void (A::*)(int) const)

因为它从未被认为是一个可行的函数,除非你明确地给出那个类型的参数,或者删除它(这里应该做什么)。

修复此问题仍然无法解决代码中的所有问题,因为如果open_file成员函数根本不存在,则可以选择后备函数,因此您需要添加以下内容(根据您的实施情况进行调整):

/* SFINAE foo-not-exists */
template <class A>
static std::false_type test(void*, ...);

作为后备:

static decltype(test(&A::open_file)) test(decltype(&A::open_file), void *)

提示:您不必提供仅在decltype()运算符内的未评估上下文中显示的函数体。

最后,当你最终将调用与void (A::*)(int) const签名匹配时,你似乎忘记了这个论点:

t.open_file(1);
//          ^

测试:

my_traits<decltype(it_does)>::eval(it_does);
my_traits<decltype(it_doesnt)>::eval(it_doesnt);

输出:

MATCHES!
open_file() not called

DEMO


使用表达式SFINAE

可以大大简化整个特征
template <class Type>
struct my_traits
{
    template <typename T>
    static auto eval(const T& t, int) -> decltype(void(t.open_file(1)))
    {
        t.open_file(1);
    }

    template <typename T>
    static void eval(const T& t, ...)
    {
        std::cout << "open_file() not called" << std::endl;
    }

    static void eval(const Type& t)
    {
        eval<Type>(t, 0);
    }
};

my_traits<decltype(it_does)>::eval(it_does);     // MATCHES!
my_traits<decltype(it_doesnt)>::eval(it_doesnt); // open_file() not called

DEMO 2


奖金问题

  

是否有可能推广这种方法?例如,给定任何函数f使用SFINAE来匹配它,使用您在DEMO 2中发布的代码,并从用户代码传递参数(例如,my_traits :: eval(it_does,参数,参数))?

template <typename T, typename... Args>
static auto call(T&& t, int, Args&&... args)
    -> decltype(void(std::forward<T>(t).open_file(std::forward<Args>(args)...)))
{
    std::forward<T>(t).open_file(std::forward<Args>(args)...);
}

template <typename T, typename... Args>
static void call(T&& t, void*, Args&&... args)
{
    std::cout << "open_file() not called" << std::endl;
}

template <typename T, typename... Args>
static void eval(T&& t, Args&&... args)
{
    call(std::forward<T>(t), 0, std::forward<Args>(args)...);
}

eval(it_does, 1);    // MATCHES!
eval(it_doesnt, 2);  // open_file() not called
eval(it_does);       // open_file() not called
eval(it_doesnt);     // DOESN'T!

DEMO 3