【问题标题】:How come pack expanded variables do not get passed by reference?为什么打包扩展变量不通过引用传递?
【发布时间】:2017-01-20 06:41:21
【问题描述】:

请考虑以下程序:

#include <iostream>


template <typename T, typename ...Ts>
struct Foo {
    template <typename ...Us>
    static void bar(T& oT0, T& oT1, const T& iT0, const T& iT1, Us... args) {
        std::cout << " -> Foo<...>::bar() enter [ " << oT0 << ", " << oT1 << " ]" << std::endl;
        Foo<T>::bar(oT0, oT1, iT0, iT1);
        Foo<Ts...>::bar(args...);
        std::cout << " <- Foo<...>::bar() exit [ " << oT0 << ", " << oT1 << " ]" << std::endl;
    }
};

template <typename T>
struct Foo<T> {
    static void bar(T& oT0, T& oT1, const T& iT0, const T& iT1) {
        std::cout << " -> Foo<>::bar() enter [ " << oT0 << ", " << oT1 << " ]" << std::endl;
        oT0 = iT0;
        oT1 = iT1;
        std::cout << " <- Foo<>::bar() exit [ " << oT0 << ", " << oT1 << " ]" << std::endl;
    }
};


int main() {
    int i0 = -1,
        i1 = 0;
    float f0 = -97.18f,
          f1 = 3.141592f;
    std::cout << "( "<< i0 << ", " << i1 << "; " << f0 << ", " << f1 << " ) " << std::endl;

    Foo<int, float, int>::bar(i0, i1, 0, 1, f0, f1, 18.f, -7.f, i0, i1, 4, 17);
    std::cout << "( "<< i0 << ", " << i1 << "; " << f0 << ", " << f1 << " ) " << std::endl;

    Foo<float>::bar(f0, f1, 18.f, -7.f);
    std::cout << "( " << f0 << ", " << f1 << " ) " << std::endl;

    Foo<float, int>::bar(f0, f1, 2.71f, 9000.1f, i0, i1, 4, 17);
    std::cout << "( "<< i0 << ", " << i1 << "; " << f0 << ", " << f1 << " ) " << std::endl;

    return 0;
}

及其注释输出(为清楚起见删除了调试输出,但可在IDEone 获得):

( -1, 0; -97.18, 3.14159 ) // initial values
( 0, 1; -97.18, 3.14159 ) // ints only set once?! floats unchanged?!
( 18, -7 ) 
( 0, 1; 2.71, 9000.1 ) // ints unchanged?!

我必须在这里遗漏一些明显的东西:从上面看,调用Foo&lt;...&gt;::bar(...) 只修改了第一组两个非常量参数。为什么main 中的下一个参数的值保持不变?

【问题讨论】:

  • 您是否使用过调试器?
  • 你的意思是Us&amp; ... args而不是Us ... args
  • @JasonC Us&amp;&amp; ... args,真的。它也需要处理 const 左值
  • @Steve:实际上我没有,我根本没有想到传递的地址不同(应该!)。
  • @jaggedSpire:你成功了!我不熟悉这种语法,你手头有没有参考?你可能也有这个作为答案。 TBH 我认为函数签名足以推断扩展类型,但我显然仍然对参数包的工作方式有误。

标签: c++ c++14 pass-by-reference variadic-templates


【解决方案1】:

您需要在模板的可变参数部分使用引用,否则前四个以外的参数将按值传递给初始调用(复制到局部变量)。随后的调用将改变那些复制的值,而不是传递的原始参数。

由于您想接受某些参数的右值,use perfect forwarding for varargs 以保留原始类型。只需更改:

static void bar(T& oT0, T& oT1, const T& iT0, const T& iT1, Us... args) {

到:

// Receive varargs as forwarding references to allow perfect forwarding instead
// of receiving them as copies
static void bar(T& oT0, T& oT1, const T& iT0, const T& iT1, Us&&... args) {

并改变:

Foo<Ts...>::bar(args...);

到:

// Per earlier link, must explicitly template std::forward call in varargs case
Foo<Ts...>::bar(std::forward<Us>(args)...); 

应该正确接收和转发可变参数,因此它们在嵌套调用中作为最里面的“真实”调用所需的任何类型(const 或非const 参考)接收。

【讨论】:

  • 我以为我掌握了可变参数模板,结果我还有很多阅读要做......谢谢。
猜你喜欢
  • 2010-10-15
  • 1970-01-01
  • 2019-05-10
  • 2013-08-22
  • 1970-01-01
  • 2015-12-29
  • 2011-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多