How to change the last argument in a parameter package?

I have a function f1

template <typename... Args> void f1(Args... args) { // the implementation is just an example, I don't really need a complicated // way to sum numbers boost::fusion::vector<Args...> v(args...); std::cout << boost::fusion::accumulate(v, 0, [](auto i1, auto i2) { return i1 + i2; }) << std::endl; } 

I want to call this a f2 function, but with another last argument. Is there a simple approach? I tried naive

 template <typename... Args> struct CallHelper; template <> struct CallHelper<> { template <typename... Args> static void Apply(Args... args) { f1(args...); } }; template <typename A0> struct CallHelper<A0> { template <typename... Args> static void Apply(Args ...args, A0 a0) { // substitute 10 to the last argument CallHelper<>::Apply(args..., 10); } }; template <typename Head, typename ...TailArgs> struct CallHelper<Head, TailArgs...> { template <typename... Args> static void Apply(Args... args, Head head, TailArgs ...tailArgs) { CallHelper<TailArgs...>::Apply(args..., head, tailArgs...); } }; template <typename... Args> void f2(Args... args) { CallHelper<Args...>::Apply(args...); } 

Of course, this does not work, because Head head not the first argument. Maybe there is a way to make Head head a parameter package? Or is there something else I can do?

+7
c ++ c ++ 11 templates variadic-templates
source share
4 answers

You can redirect your arguments as a tuple, and then unpack everything except the last one using std::integer_sequence . This code looks a lot simpler than your approach:

 template<typename... Args> void f1(Args... args) { boost::fusion::vector<Args...> v(args...); std::cout << boost::fusion::accumulate(v, 0, [](auto i1, auto i2) { return i1 + i2; }) << std::endl; } template<typename Tuple, size_t... idx> void callImpl(Tuple&& tuple, std::index_sequence<idx...>) { f1(std::get<idx>(std::forward<Tuple>(tuple))..., 10); } template<typename... Ts> void callWithLast10(Ts&&... ts) { callImpl(std::forward_as_tuple(ts...), std::make_index_sequence<sizeof...(Ts) - 1>()); } 

Using:

 f1(1, 2, 3, 4); // Prints 10 callWithLast10(1, 2, 3, 4); // Prints 16 
+5
source share

Using index sequences ...

 #include <utility> #include <iostream> template <typename ... Args> void f1 (Args ... args) { using unused=int[]; (void)unused { 0, (std::cout << args << ", ", 0)... }; std::cout << std::endl; } template <std::size_t> struct getVal { template <typename T1, typename T2> T2 operator() (T1 const &, T2 const & t2) { return t2; } }; template <> struct getVal<0U> { template <typename T1, typename T2> T1 operator() (T1 const & t1, T2 const &) { return t1; } }; template <std::size_t ... Is, typename ... Args> void f2_helper (std::index_sequence<Is...> const &, Args const & ... args) { f1 ( getVal<sizeof...(Is)-Is-1U>()(10, args)... ); } template <typename ... Args> void f2 (Args ... args) { f2_helper(std::make_index_sequence<sizeof...(Args)>{}, args...); } int main() { f1(1, 2L, 3.3, "ten"); // print 1, 2, 3.3, ten, f2(1, 2L, 3.3, "ten"); // print 1, 2, 3.3, 10, } 

This solution is in C ++ 14 (requires std::index_sequence and std::make_index_sequence ), but should just create substitutes for C ++ 11 if you need it.

+2
source share

Just to post what I mentioned in the comments

 #include <utility> #include <iostream> template<bool b, typename T1, typename T2> decltype(auto) replace_if(T1&& t1, T2&& t2) { if constexpr(b) return std::forward<T1>(t1); else return std::forward<T2>(t2); } template<typename... Args> void f1(Args&&... args) { (std::cout << ... << args) << std::endl; } template<typename T, typename... Args, size_t... I> decltype(auto) replace_last_impl(std::index_sequence<I...>, T&& t, Args&&... args) { return f1(replace_if<sizeof...(Args) - 1 == I>(std::forward<T>(t), std::forward<Args>(args))...); } template<typename T, typename... Args> decltype(auto) replace_last(T&& t, Args&&... args) { return replace_last_impl(std::index_sequence_for<Args...>{}, std::forward<T>(t), std::forward<Args>(args)...); } int main() { f1(1, 2, 3); // 123 replace_last("three", 1, 2, 3); // 12three } 

The show star is replace_if , which is a pretty general way of converting a parameter package.

0
source share

There are simple ways to do this, including recording a few problematic functions just to solve this problem.

I do not like this.

So, first I write some helper functions. nth takes an index and a bunch of arguments and returns nth one of them:

 template<std::size_t I, class...Args> decltype(auto) nth( Args&&... args ) { return std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...)); } 

index_over and index_upto allow you to extend the size_t inline parameter packages in your function. This eliminates the need to create helper functions only for unpacking:

 template<std::size_t...Is> auto index_over( std::index_sequence<Is...> ) { return [](auto&& f)->decltype(auto) { return decltype(f)(f)( std::integral_constant< std::size_t, I >{} ); }; } template<std::size_t N> auto index_upto( std::integral_constant< std::size_t, N > ={} ) { return index_over( std::make_index_sequence<N>{} ); } 

Then we write our f2 :

 template<class...Args> void f2( Args&&... args ) { index_upto< sizeof...(args)-1 >()( [&](auto...Is) { f1( nth<Is>(std::forward<Args>(args)...)..., 10 ); } ) } 

and done.

This generates quadratic amounts of unused links that a good compiler can optimize, but takes time to do this (at compile time).

0
source share

All Articles