【问题标题】:Matching member function existence and signature: parameters匹配成员函数存在和签名:参数
【发布时间】:2014-12-24 06:32:50
【问题描述】:

阅读相关问题"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?",我正在实现自己的特征类。目标很简单,尽管我无法实现我想要的:提供一个特征类,将调用静态重定向到匹配的类

所以,如果我提供给我的特征的类有,例如void open_file() 方法,它会调用它,否则使用特征函数(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 的只是检查成员的存在,它可以工作。接下来,我尝试将它用于静态SFINAEif/elseie,如果成员函数存在则调用它,否则使用预定义的函数,没有成功(如我所说,我不是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(),例如具有匹配template &lt;class T&gt; open_file(T t) 的SFINAE?

感谢和干杯!

【问题讨论】:

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


    【解决方案1】:

    问题是你打电话给test(&amp;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, parameter, parameter))? p>

    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

    【讨论】:

    • 第二个选项很有意思!您认为可以推广这种方法吗?例如,给定任何函数 f 使用 SFINAE 匹配它,使用您在 DEMO 2 中发布的代码,并从用户代码中传递参数(例如,my_traits&lt;decltype(it_does)&gt;::eval(it_does, parameter, parameter))?
    • 谢谢@piotr-s,太棒了!如果我没有看错发布程序集,则没有运行时开销,因为我只看到invoke void @_ZNK3seq10does_match9open_fileEi(%"class.seq::does_match"* %it_does, i32 4321),但是,我看到一些对call void @llvm.dbg.value(metadata !{%"class.seq::does_match"* %it_does}, i64 0, metadata !13069), !dbg !13059 的调用,但我认为这不会影响我的代码。我是否朝着正确的方向解释了这一点?非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多