具有两个可变参数的函数

时间:2017-10-19 08:49:38

标签: c++ c++11 templates variadic-templates template-meta-programming

我打算实现一个模板函数,它接受两个函数及其参数列表,然后创建两个包装器。我已经实现了类似的解决方案来接受两个函数并使用std::bind()创建包装器但使用硬编码参数。解决方案是这样的:

template <typename T1, typename T2, typename T3, typename T4>
myWrapper (T2 func1, T4 func2) {
  std::function<T1>ff = std::bind(func1, 1, placeholders::_1);
  std::function<T3>ff = std::bind(func1, 110, 20, "something");
}

如您所见,std::bind()的参数在两种情况下都是硬编码的,我希望通过myWrapper()函数读取它们。有没有办法读取两个可变参数模板(任意长度和类型的变量列表)并将它们发送到上面代码中的两个std::bind()调用?

谢谢,

3 个答案:

答案 0 :(得分:2)

关于variadicStruct的想法,您可以查看std::tuple

template<class... Ts, class... Us>
void do_stuff(const std::tuple<Ts...>&, const std::tuple<Us...>&) {
  std::cout << "two variadic packs with " << sizeof...(Ts)
        << " and " << sizeof...(Us) << " elements." << std::endl;
}

这样称呼:

do_stuff(std::make_tuple(4.7, 'x', 1.0, 4, 8l), std::make_tuple("foo", 1));

答案 1 :(得分:2)

不确定你想要什么,但我想你需要使用std::tuple(或类似的东西)来分隔两个args序列。

以下是我的意思的完整工作示例,根据我对您想要的内容的理解。

#include <tuple>
#include <string>
#include <utility>
#include <iostream>
#include <functional>

template <typename T1, typename T2, typename T3, typename T4,
          typename ... Ts1, typename ... Ts2,
          std::size_t ... Is1, std::size_t ... Is2>
auto myWrapperH (T3 func1, T4 func2, std::tuple<Ts1...> const & tp1,
                 std::tuple<Ts2...> const & tp2,
                 std::index_sequence<Is1...> const &,
                 std::index_sequence<Is2...> const &)
 {
   T1 f1 = std::bind(func1, std::get<Is1>(tp1)...);
   T2 f2 = std::bind(func2, std::get<Is2>(tp2)...);

   return std::make_pair(f1, f2);
 }

template <typename T1, typename T2, typename T3, typename T4,
          typename ... Ts1, typename ... Ts2>
auto myWrapper (T3 func1, T4 func2, std::tuple<Ts1...> const & tp1,
                std::tuple<Ts2...> const & tp2)
 { return myWrapperH<T1, T2>(func1, func2, tp1, tp2,
                             std::make_index_sequence<sizeof...(Ts1)>{},
                             std::make_index_sequence<sizeof...(Ts2)>{}); }

int foo (int a, int b)
 { return a+b; }

std::size_t bar (int a, int b, std::string const & str)
 { return str.size() + a + b; }

int main ()
 {
   using fType1 = std::function<int(int)>;
   using fType2 = std::function<long()>;

   auto mwr = myWrapper<fType1, fType2>(&foo, &bar,
                 std::make_tuple(1, std::placeholders::_1),
                 std::make_tuple(110, 20, std::string{"something"}));

   std::cout << mwr.first(5) << std::endl; // print   6
   std::cout << mwr.second() << std::endl; // print 139
 }

不幸的是C ++ 14代码(auto返回类型; std::index_sequencestd::make_index_sequence),但在C ++ 11中应该很容易适应。

- 编辑 -

正如Banan指出的那样(谢谢!),没有必要明确返回函数的类型(T1T2)。

使用auto返回类型,示例可以简化如下

#include <tuple>
#include <string>
#include <utility>
#include <iostream>
#include <functional>

template <typename F1, typename F2, typename ... Ts1, typename ... Ts2,
          std::size_t ... Is1, std::size_t ... Is2>
auto myWrapperH (F1 func1, F2 func2, std::tuple<Ts1...> const & tp1,
                 std::tuple<Ts2...> const & tp2,
                 std::index_sequence<Is1...> const &,
                 std::index_sequence<Is2...> const &)
 { return std::make_pair(std::bind(func1, std::get<Is1>(tp1)...),
                         std::bind(func2, std::get<Is2>(tp2)...)); }

template <typename F1, typename F2, typename ... Ts1, typename ... Ts2>
auto myWrapper (F1 func1, F2 func2, std::tuple<Ts1...> const & tp1,
                std::tuple<Ts2...> const & tp2)
 { return myWrapperH(func1, func2, tp1, tp2,
                     std::make_index_sequence<sizeof...(Ts1)>{},
                     std::make_index_sequence<sizeof...(Ts2)>{}); }

int foo (int a, int b)
 { return a+b; }

std::size_t bar (int a, int b, std::string const & str)
 { return str.size() + a + b; }

int main ()
 {
   auto mwr = myWrapper(&foo, &bar,
                 std::make_tuple(1, std::placeholders::_1),
                 std::make_tuple(110, 20, std::string{"something"}));

   std::cout << mwr.first(5) << std::endl; // print   6
   std::cout << mwr.second() << std::endl; // print 139
 }

答案 2 :(得分:1)

您可以使用std::make_tuplestd::apply,例如:

template <typename R, typename Func1, typename Args1>
void wrapper (Func1 f1, Args1 a1)
{
    std::function<R> wrapped1 = [f1, a1]{ return std::apply(f1, a1); };
}

// ...

auto deg_to_rad = [](int x){ return x / 180.f * 3.1415f; };
wrapper<float>(deg_to_rad, std::make_tuple(45));

std::apply需要C ++ 17支持。但是,linked page on cppreference包含一个可在C ++ 11中使用的实现。