【问题标题】:How to change the last argument in the parameter pack?如何更改参数包中的最后一个参数?
【发布时间】:2017-06-20 16:57:45
【问题描述】:

我有一个函数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 成为参数包?或者还有什么我可以做的吗?

【问题讨论】:

  • 试试template &lt;typename... Args,typename Last&gt;
  • 我现在无法使用 PC,但可以尝试将 conditional_t&lt;integer_sequence == n, Args, int&gt;...sizeof...(Args) 结合起来,对于变量,写一个 replace_if&lt;condition&gt;(T, int)if constexpr

标签: c++ templates c++14 variadic-templates generic-lambda


【解决方案1】:

您可以将您的参数作为元组转发,然后使用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

【讨论】:

  • 哦,这就是我解包元组的方法。我不知道。 Stackoverflow 搜索提供了更长的解决方案。谢谢!
【解决方案2】:

借助索引序列...

#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_sequencestd::make_index_sequence),但如果需要,应该很容易创建 C++11 的替代品。

【讨论】:

  • 这很聪明。我大概花了 10 分钟才明白。谢谢!
  • @noxmetus - 一个问题:最后一个值替换为相同类型的值?在这种情况下,有一个更简单的解决方案。
  • 是的,是同一种类型。
  • @noxmetus - 给我几分钟,我会尝试改进我的答案
  • @noxmetus - 回答改进
【解决方案3】:

只是为了发布我在 cmets 中提到的内容

#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,这是一种非常通用的参数包转换方式。

【讨论】:

    【解决方案4】:

    有一些简单的方法可以做到这一点,包括编写多个特定于问题的函数来解决这个问题。

    我不喜欢那样。

    所以首先我编写了一些辅助函数。 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_overindex_upto 让您在函数中扩展size_tinline 参数包。这样就不必为了解包而创建辅助函数:

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

    完成了。

    这确实会生成二次数量的未使用引用,一个好的编译器可以优化掉这些引用,但需要时间(在编译时)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-31
      • 2010-11-15
      • 2014-07-23
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 2014-07-31
      • 2016-12-22
      相关资源
      最近更新 更多