Как изменить последний аргумент в пакете параметров?

У меня есть функция 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 пакет параметров, а? Или есть что-то еще, что я могу сделать?

7

Решение

Вы можете переслать свои аргументы как кортеж, а затем распаковать все, кроме последнего, используя 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
5

Другие решения

С помощью индексных последовательностей …

#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, если они вам нужны.

2

Просто чтобы опубликовать то, что я упомянул в комментариях

#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, что является довольно общим способом преобразования пакета параметров.

0

Есть простые способы сделать это, включая написание нескольких специфических для задачи функций, просто чтобы решить эту проблему.

Мне это не нравится

Итак, сначала я пишу некоторые вспомогательные функции. 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 );
}
)
}

и сделано.

Это генерирует квадратичное количество неиспользуемых ссылок, которые хороший компилятор может оптимизировать, но для этого требуется время (во время компиляции).

0
По вопросам рекламы [email protected]