代码之家  ›  专栏  ›  技术社区  ›  Aryan

仅替换成员函数的模板语法

  •  1
  • Aryan  · 技术社区  · 7 年前

    我试图创建一个函数重载,使其仅绑定(适用于)一个成员函数。我看了一下函数签名 std::mem_fn http://en.cppreference.com/w/cpp/utility/functional/mem_fn

    template <class Ret, class T>
    /* unspecified */ mem_fn (Ret T::* pm);
    

    所以我就这样构造了我的参数

    template <typename R, typename F>
    auto call_me(R C::* func) {
        return (mContainer.*func);
    }
    

    然而,我得到了这个错误

    .\template.cpp: In function 'int main()':
    .\template.cpp:29:49: error: no matching function for call to 'MyClass<int, std::vector<int> >::call_me(std::vector<int>::size_type (std::vector<int>::*)() const noexcept)'
         cout << test.call_me(&std::vector<int>::size) << endl;
                                                     ^
    .\template.cpp:16:10: note: candidate: template<class R, class F> auto MyClass<T, C>::call_me(R C::*) [with R = R; F = F; T = int; C = std::vector<int>]
         auto call_me(R C::* func) {
              ^~~~~~~
    .\template.cpp:16:10: note:   template argument deduction/substitution failed:
    .\template.cpp:29:49: note:   couldn't deduce template parameter 'F'
         cout << test.call_me(&std::vector<int>::size) << endl;
    

    我尝试这样做的原因是,我可以有一个适用于一般lambda和函数对象的重载和另一个适用于成员函数指针的重载。这是我试图实现的一个最小示例。我知道这个问题有点困惑,所以如果需要的话,请随时要求澄清。

    #include <vector>
    #include <iostream>
    
    using namespace std;
    
    template <typename T, typename C>
    struct MyClass {
        // This doesnt work because of SFINAE
    
        template <typename F, typename... A>
        auto call_me(F func, A... args) { // lambda version
            return func(args...);
        }
    
        template <typename R, typename F>
        auto call_me(R C::* func) { // member function version
            return (mContainer.*func);
        }
    
        C mContainer; // this is private in my actual code
    
    };
    
    
    int main() {
        MyClass<int, std::vector<int> > test;;
    
        // these two calls will call the member function version of the overload
        cout << test.call_me(&std::vector<int>::size) << endl;
    
        using insert_func_t = std::vector<int>::iterator(std::vector<int>::*)(std::vector<int>::const_iterator, const int&);
        test.call_me(static_cast<insert_func_t>(&std::vector<int>::insert), test.mContainer.begin(), 4);
    
        // this call will call the lambda version of the overload
        cout << test.call_me([](std::vector<int>& in){ in.push_back(5); });
    
        return 0;
    }
    
    2 回复  |  直到 7 年前
        1
  •  4
  •   chris    7 年前

    你可以用 std::invoke :

    template <typename F, typename... A>
    auto call_me(F func, A... args) { // lambda version
        return std::invoke(func, mContainer, args...);
    }
    

    operator() . 对于成员函数,它用要使用的对象作为参数的前缀,然后用适当的语法调用它。换句话说,你的工作已经完成了。

    您还可以考虑完美转发:

    template <typename F, typename... A>
    auto call_me(F&& func, A&&... args) { // lambda version
        return std::invoke(std::forward<F>(func), mContainer, std::forward<A>(args)...);
    }
    
        2
  •  0
  •   Sam Varshavchik    7 年前

    函数(或类方法)不仅仅是返回类型。

    函数有参数。每个参数都有一个类型。函数参数的类型是函数签名的一部分。这包括具有零参数的函数。

    如果希望模板函数绑定到任意类的成员函数,则模板函数也必须显式绑定到类成员函数的参数类型。这是C++类型安全的一个基本方面:

    template<class Ret,
         class T,
         class ...Args>
    void mem_fn(Ret (T::*pm)(Args...))
    {
    }
    
    class foo {
    
    public:
    
        void bar(int);
    };
    
    void foo()
    {
        mem_fn(&foo::bar);
    }
    

    如果您只想绑定到不带参数的成员函数,则可以放弃可变模板参数,并且必须相应地显式绑定模板:

    template<class Ret,
         class T>
    void mem_fn(Ret (T::*pm)())
    {
    }
    
    class foo {
    
    public:
    
        void bar();
    };
    
    
    void foo()
    {
        mem_fn(&foo::bar);
    }
    

    请注意,此模板将仅绑定到不带参数的类成员函数。它不会绑定到 void bar(int) 例如,成员函数。