2017-10-16 79 views
1

我试图创建一个函数重载因此它只能结合(工程)成员函数。我接过来一看在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和功能对象的重载以及适用于membe的另一个重载r功能指针。这是我想要实现的一个最简单的例子。我知道这个问题有点令人困惑,所以如果需要的话请随时要求澄清。

#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; 
} 
+1

'的std :: invoke'提供统一的调用语法。如果你还没有在标准库中使用它,它不需要编写C++ 17功能,而且现在有独立的实现。通过使用'invoke'函数,您不需要通过调用差异来污染其余的代码。 – chris

+0

@克里斯我只是检查了这一点,但它仍然给我留下搞清楚,如果提供的参数是一个成员函数或它的一个可调用对象的问题。 – Aryan

+0

@IgorTandetnik嗯,我有这个尝试也行,但它仍然无法正常工作的r(C :: * FUNC)()'和'用住宅(丙类:: * FUNC)(参数...)'用'Args'是基于您的代码的另一模板参数 – Aryan

回答

4

可以覆盖这两种情况下与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)...); 
} 
0

还有更多的功能(或类方法),比它的返回类型。

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

如果你想有一个模板函数绑定到任意类的成员函数,模板函数必须明确地绑定到类的成员函数的参数类型,也。这是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)

+0

和cv/ref限定符(和C省略号) - >'Ret(C :: *)(参数...,...)const volatile &&'。 – Jarod42

+0

@ Jarod42我是否必须为每一个重载? (const,const volatile,const noexcept ...等)?还是有办法简化它? – Aryan

+0

@Aryan:如果你不依赖已经处理过的东西(作为'std :: invoke'),你将不得不自己处理它。 – Jarod42