【问题标题】:How to create variadic template function to shift the argument values and handles both lvalues and rvalues?如何创建可变参数模板函数来移动参数值并同时处理左值和右值?
【发布时间】:2017-08-16 19:00:16
【问题描述】:

我想用实现相同功能的可变参数模板替换这些宏。

#define SHFT2( a, b, c ) do { (a) = (b); (b) = (c); } while(0)
#define SHFT3( a, b, c, d ) do { (a) = (b); (b) = (c); (c) = (d); } while(0)
#define SHFT4( a, b, c, d, e ) do { (a) = (b); (b) = (c); (c) = (d); (d) = (e); } while(0)

我有一个适用于左值的解决方案

template<typename T, typename... Ts>
T first(T t, Ts... ts)
{
    return t;
}

template<typename T>
void shift(T t)
{
    // do nothing
}

template<typename T, typename... Ts>
void shift(T& t, Ts&... ts)
{
    t = first(ts...);
    shift(ts...);
}

例如,这是可行的

int w = 1;
int x = 2;
int y = 3;
int z = 4;

shift(w, x, y, z);

printf("%d %d %d %d\n", w, x, y, z); // 2 3 4 4

但我希望能够在最后转移一个右值

shift(w, x, y, z, 5);

printf("%d %d %d %d\n", w, x, y, z); // expect 2 3 4 5

我收到此错误

test.cpp:31:2: error: no matching function for call to 'shift'
        shift(w, x, y, z, 5);
        ^~~~~
test.cpp:16:6: note: candidate function [with T = int, Ts = <int, int, int, int>] not viable: expects an l-value for 5th
      argument
void shift(T& t, Ts&... ts)
     ^
test.cpp:10:6: note: candidate function template not viable: requires single argument 't', but 5 arguments were provided
void shift(T t)

因为你不能引用右值。

我怎样才能在这两种情况下都做到这一点?

【问题讨论】:

  • 你提到的宏呢?我想它会产生同样的错误。无论如何,将某些东西分配给像 5 这样的文字是没有意义的。或者,您是否打算仅在最后一个位置放置右值?
  • @JunekeyJeon 正确。在最后一个位置只会有一个右值。

标签: c++ c++11 templates variadic-templates variadic-functions


【解决方案1】:

您可以使用转发引用参数来接受左值和右值,并std::forward“转发”原始参数的值类别,即,将参数转换为匹配价值类别。

template <typename T>
void shift(T&& t) {
    // do nothing
}

template<typename T1, typename T2, typename... Ts>
void shift(T1&& t1, T2&& t2, Ts&&... ts) {
    std::forward<T1>(t1) = std::forward<T2>(t2);
    shift(std::forward<T2>(t2), std::forward<Ts>(ts)...);
}

这里,std::forward&lt;T1&gt;(t1) 确保如果参数是左值,t1 将被分配为左值,如果参数是右值,则分配为右值。例如,shift(42, x) 将无法编译,因为无法分配 int 类型的右值。

std::forward&lt;T2&gt;(t2) 确保如果t2 的参数是左值,它将被复制,而如果它是右值,则尽可能移动它。

std::forward&lt;T2&gt;(t2)std::forward&lt;Ts&gt;(ts)... 将值类别信息传递给递归调用。

【讨论】:

  • 将此标记为正确,因为我认为它在误用时会给出最好/最早的错误消息:test.cpp:14:23: error: expression is not assignable std::forward&lt;T1&gt;(t1) = std::forward&lt;T2&gt;(t2);
【解决方案2】:

你会想要类似的东西

#include <utility>
// abort
template <class T> void shift(T&&) { }
// assign to lvalue
template<class T1, class T2, class... Ts>
void shift(T1& t1, T2&& t2, Ts&&... ts) 
{
    t1 = std::forward<T2>(t2);
    shift(std::forward<T2>(t2), std::forward<Ts>(ts)...);
}

它期望除最后一个之外的所有参数都是左值,最后一个也可能是左值。

  • 一旦除最后一个参数之外的所有参数都被消耗完,就会调用 abort shift(T&amp;&amp;)
  • 所有其他参数在某个点作为左值引用转发为t1,确保左值已在除最后一个之外的任何插槽中传递。

shift(w, x, y, z, 5); 编译但 shift(w, x, y, 5, z);shift(w, x, y, 5, 5); 不编译。

【讨论】:

    【解决方案3】:

    因为你不能引用右值。

    您将参数声明为左值引用,只需根据传入参数的值类别将它们更改为forwarding references,它既可以用作左值引用,也可以用作右值引用。例如

    template<typename T, typename... Ts>
    T&& first(T&& t, Ts&&... ts)
    {
        return std::forward<T>(t);
    }
    
    template<typename T>
    void shift(T&& t)
    {
        // do nothing
    }
    
    template<typename T, typename... Ts>
    void shift(T&& t, Ts&&... ts)
    {
        t = first(std::forward<Ts>(ts)...);
        shift(std::forward<Ts>(ts)...);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      • 2013-07-12
      • 1970-01-01
      相关资源
      最近更新 更多