4

我正在尝试编写一个模板类的函数,它接受一个参数,该参数是大类的私有数据中的成员类的函数指针。当你调用该成员时,它会在较小的类上调用该函数。 (?混淆右)为了证明,我这里有一个非工作例如:模板类中的模板variadic函数不会编译

#include <vector> 
#include <iostream> 

using namespace std; 

template <typename T, typename C> 
struct MyClass { 

    template <typename F, typename... A> 
    auto call_me(F func, A... args) { // pass in the function we want to call 
     return (mContainer.*func) (args...); // call the function supplied by 
     // the parameter on the private member data 
    } 

    C mContainer; // this will be private in my actual code 

}; 


int main() { 
    MyClass<int, std::vector<int> > test;; 

    cout << test.call_me(&std::vector<int>::size) << endl; // works 
    test.call_me(&std::vector<int>::insert, test.mContainer.begin(), 4); // doesn't work 

    return 0; 
} 

请注意,这不是我的实际代码但我想要做的一个小例子。正如你所看到的,我正在试图呼叫size“Private”的成员函数(我已经在此处公开示范)vectorMyClass中的类。这只能每当我有没有参数编译器进行解包,但是当我尝试做插入功能(其参数解包),编译器给我的错误:

.\template.cpp: In function 'int main()': 
.\template.cpp:24:71: error: no matching function for call to 'MyClass<int, std::vector<int> >::call_me(<unresolved overloaded function type>, std::vector<int>::iterator, int)' 
    test.call_me(&std::vector<int>::insert, test.mContainer.begin(), 4); 
                    ^
.\template.cpp:10:10: note: candidate: template<class F, class ... A> auto MyClass<T, C>::call_me(F, A ...) [with F = F; A = {A ...}; T = int; C = std::vector<int>] 
    auto call_me(F func, A... args) { // pass in the function we want to call 
      ^~~~~~~ 
.\template.cpp:10:10: note: template argument deduction/substitution failed: 
.\template.cpp:24:71: note: couldn't deduce template parameter 'F' 
    test.call_me(&std::vector<int>::insert, test.mContainer.begin(), 4); 

这是相同的错误我得到了我的实际生产代码,调用variadic函数没有参数来解压缩工作,但如果我给予更多,我得到相同的错误信息。这是我第一次使用Variadic模板的真正尝试,所以任何建议和帮助将不胜感激。

回答

6

这里的问题是insert是一个重载函数。编译器没有试图解决模板参数推导中需要的重载,因为它无法知道。您必须将该函数强制转换为您要使用的重载类型,以便为其指定类型。这看起来就像

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); 

一般是

static_cast<return_type(class_name::*)(function_parameters)>(&class_name::function_name) 

另一种办法是改变功能一点,并采取表达你想要做什么拉姆达。这看起来就像

template <typename T, typename C> 
struct MyClass { 

    template <typename F, typename... A> 
    auto call_me(F func, A... args) { // pass in the function we want to call 
     return func(mContainer, args...); // call the function supplied by 
     // the parameter on the private member data 
    } 

    C mContainer; // this will be private in my actual code 
}; 

int main() { 
    MyClass<int, std::vector<int> > test;; 

    test.call_me([](auto& container, auto... args){ container.insert(args...); }, test.mContainer.begin(), 4); 

    return 0; 
} 
+0

我怎么会去这样做?谷歌搜索引导我使用static_cast,这不起作用。 'test.call_me(static_cast :: iterator,int)>(&std :: vector :: insert),test.mContainer.begin(),4);'。有没有其他方法可以做到这一点? – Aryan

+0

@Aryan我刚刚添加演员到答案 – NathanOliver

+0

谢谢你,这工作。有没有更美丽的方式来做到这一点?或者使用较少按键的东西? – Aryan

0

基本上你不能拿一个悬而未决的重载函数的地址,因为编译器将无法选择正确的函数入口地址。在正常的函数调用期间,编译器会解析重载的函数,但是像yours或std :: bind()这样的模板将不起作用,因为这些参数用于调用模板函数,而不是您想要调用的函数。

您可以手动解决这样的过载:

using ftype = std::vector<int>::iterator(std::vector<int>::*) 
     (std::vector<int>::const_iterator, const std::vector<int>::value_type&); 
    test.call_me((ftype)(&std::vector<int>::insert), test.mContainer.begin(), 4); // works 
0

它更容易做这种事情时,要处理的函数对象。它将方法重载的问题卸载到编译器中。

Lambda表达式也行(他们是函数对象):

#include <vector> 
#include <iostream> 

template <typename T, typename C> 
struct MyClass { 

    template <typename F, typename... A> 
    auto call_me(F func, A&&... args) -> decltype(auto) 
    { // pass in the function we want to call 
     return func(mContainer, std::forward<A>(args)...); // call the function supplied by 
     // the parameter on the private member data 
    } 

    C mContainer; // this will be private in my actual code 

}; 
/* 
* It's often easier to deal in function objects 
*/ 
struct insert 
{ 
    template<class Container, class...Args> 
    decltype(auto) operator()(Container& cont, Args&&...args) const 
    { 
     return cont.insert(std::forward<Args>(args)...); 
    } 
}; 

struct size 
{ 
    template<class Container, class...Args> 
    decltype(auto) operator()(Container& cont) const 
    { 
     return cont.size(); 
    } 
}; 

int main() { 
    MyClass<int, std::vector<int> > test;; 

    std::cout << test.call_me(size()) << std::endl; // works 
    test.call_me(insert(), test.mContainer.begin(), 4); // doesn't work 

    // or lambdas 
    auto insert2 = [](auto& container, auto&&...args) -> decltype(auto) 
    { 
     return container.insert(std::forward<decltype(args)>(args)...); 
    }; 
    test.call_me(insert2, test.mContainer.begin(), 5); 


    return 0; 
}