У меня есть функция 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;
}
Я хочу вызвать это из функции f2()
, но с другим последним аргументом. Есть ли простой подход? Я попробовал наивный
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...);
}
Конечно, это не работает, потому что Head head
это не первый аргумент. Может быть, есть способ сделать Head head
пакет параметров, а? Или есть что-то еще, что я могу сделать?
Вы можете переслать свои аргументы как кортеж, а затем распаковать все, кроме последнего, используя std::integer_sequence
, Этот код выглядит намного проще, чем ваш подход:
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>());
}
Использование:
f1(1, 2, 3, 4); // Prints 10
callWithLast10(1, 2, 3, 4); // Prints 16
С помощью индексных последовательностей …
#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,
}
Это решение C ++ 14 (требуется std::index_sequence
а также std::make_index_sequence
) но должно быть просто создать заменители для C ++ 11, если они вам нужны.
Просто чтобы опубликовать то, что я упомянул в комментариях
#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
}
Звезда шоу replace_if
, что является довольно общим способом преобразования пакета параметров.
Есть простые способы сделать это, включая написание нескольких специфических для задачи функций, просто чтобы решить эту проблему.
Мне это не нравится
Итак, сначала я пишу некоторые вспомогательные функции. nth
принимает индекс и группу аргументов и возвращает nth
один из них:
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
а также index_upto
позволит вам расширить пакеты параметров size_t
«s в соответствии в вашей функции. Это избавляет от необходимости создавать вспомогательные функции только для распаковки:
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>{} );
}
Затем мы пишем наши f2
:
template<class...Args>
void f2( Args&&... args ) {
index_upto< sizeof...(args)-1 >()(
[&](auto...Is) {
f1( nth<Is>(std::forward<Args>(args)...)..., 10 );
}
)
}
и сделано.
Это генерирует квадратичное количество неиспользуемых ссылок, которые хороший компилятор может оптимизировать, но для этого требуется время (во время компиляции).