C++ 为什么包扩展变量不能通过引用传递? 请考虑以下程序: #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; }

C++ 为什么包扩展变量不能通过引用传递? 请考虑以下程序: #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; },c++,c++14,pass-by-reference,variadic-templates,C++,C++14,Pass By Reference,Variadic Templates,这里我肯定遗漏了一些明显的东西:从上面看,调用Foo::bar(…)只会修改第一组两个非常量参数。为什么下一个参数的值在main中保持不变?您需要在模板的varargs部分使用引用,或者前四个参数之外的参数将通过值传递给初始调用(复制到局部变量)。随后的调用将改变那些复制的值,而不是传递的原始参数 由于您希望为某些参数接受右值,因此需要保留原始类型。只要改变一下: static void bar(T& oT0, T& oT1, const T& iT0, const T&

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

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

由于您希望为某些参数接受右值,因此需要保留原始类型。只要改变一下:

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

和变化:

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

//对于前面的链接,必须在varargs情况下显式地模板std::forward调用
Foo::bar(std::forward(args)…);

它应该正确地接收和转发vararg,以便在嵌套调用中以最内层的“real”类型(
const
或非
const
引用)接收vararg调用需要。

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

由于您希望为某些参数接受右值,因此需要保留原始类型。只要改变一下:

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

和变化:

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

//对于前面的链接,必须在varargs情况下显式地模板std::forward调用
Foo::bar(std::forward(args)…);

它应该正确地接收和转发vararg,以便在嵌套调用中以最内部的“真实”调用所需的任何类型(
const
或非
const
引用)接收vararg。

您是否使用了调试器?您的意思是
Us&。。。args
而不是
Us。。。args
?@JasonC
Us&&。。。args
,真的。它需要处理常量左值too@Steve:事实上我没有,只是我没有想到传递的地址是不同的(应该是!)。@jaggedSpire:你搞定了!我不熟悉这种语法,你手头有参考资料吗?你也可以这样回答。TBH我认为函数签名足以推断扩展类型,但我显然仍然错误地理解参数包的工作方式。你是否使用了调试器?你是说
Us&。。。args
而不是
Us。。。args
?@JasonC
Us&&。。。args
,真的。它需要处理常量左值too@Steve:事实上我没有,只是我没有想到传递的地址是不同的(应该是!)。@jaggedSpire:你搞定了!我不熟悉这种语法,你手头有参考资料吗?你也可以这样回答。虽然我认为函数签名足以推断扩展类型,但我显然仍然错误地理解了参数包的工作原理。我认为我掌握了可变模板,结果我还有很多阅读要做。。谢谢。我以为我掌握了可变模板,结果发现我还有很多阅读要做。。谢谢
// Per earlier link, must explicitly template std::forward call in varargs case
Foo<Ts...>::bar(std::forward<Us>(args)...);