【问题标题】:Template variadic function in template class won't compile模板类中的模板可变参数函数不会编译
【发布时间】:2018-03-25 13:22:03
【问题描述】:

我正在尝试为模板类编写一个函数,该函数接受一个参数,该参数是大类私有数据中成员类的函数指针。当您调用该成员时,它会在较小的类上调用该函数。 (令人困惑的权利?)为了演示,我在这里有一个不工作的例子:

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

请注意,这不是我的实际代码,而是我正在尝试做的一个小例子。如您所见,我正在尝试调用MyClass 中的'Private' 的size 成员函数(我在此处将其公开以进行演示)vector 类中的MyClass。这仅在我没有编译器要解包的参数时才有效,但是当我尝试执行插入函数(有要解包的参数)时,编译器会给我一个错误:

.\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 模板,因此我们将不胜感激任何建议和帮助。

【问题讨论】:

    标签: c++ templates function-pointers variadic-templates member-function-pointers


    【解决方案1】:

    这里的问题是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)
    

    另一种选择是稍微更改函数并采用表达您想要完成的操作的 lambda。看起来像

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

    【讨论】:

    • 我该怎么做呢?谷歌搜索导致我使用 static_cast,它不起作用。 test.call_me(static_cast&lt;void (*) (std::vector&lt;int&gt;::iterator, int)&gt;(&amp;std::vector&lt;int&gt;::insert), test.mContainer.begin(), 4);。还有其他方法吗?
    • @Aryan 我刚刚将演员添加到答案中
    • 谢谢你,成功了。有没有更漂亮的方法呢?或者使用较少击键的东西?
    • @Aryan 我不确定。如果您要经常使用它们,您可以给其中一些名称加上别名。另一种选择是稍微改变它并传递一个接受容器和参数的 lambda,然后将它们一起调用,这将允许重载解析为您工作。
    • @Aryan 我刚刚用 lambda 方法更新了答案。
    【解决方案2】:

    基本上,您不能获取未解析的重载函数的地址,因为编译器将无法选择正确的函数入口点地址。在正常的函数调用期间,编译器会解析重载函数,但是对于像你这样的模板或 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
    

    【讨论】:

      【解决方案3】:

      在做这种事情的时候处理函数对象比较容易。它将方法重载的问题转嫁给编译器。

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

      【讨论】:

        猜你喜欢
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-27
        • 2013-09-14
        • 2011-06-29
        相关资源
        最近更新 更多