通用参考 l 值不复制对象



为什么这些断言适用于下面的代码?通用引用应绑定到 l 值引用run(T& a),并从a复制对象b。但是,两个对象地址"a"和"b"在run()函数中是相同的。使用 C++11/14/17/2a gcc-9.2 和 clang++-6.0 进行测试。标准的哪一部分说这是有效的?没有找到任何相关的东西。

#include <cassert>
#include <utility>
template <typename T>
void run(T&& a)
{
T b {std::forward<T>(a)};
++b;
assert(b == a);
assert(&a == &b);
}
int main()
{
int value {10};
run(value); // asserts work, not expected
// run(std::move(value)); // the asserts don't work as expected
}

但是,两个对象地址"a"和"b"在run()函数中是相同的。

当传递左值时,T被推导为左值引用,即int&.(int& &&折叠为int&,因此函数参数a的类型为int&。然后将b声明为与a绑定的引用。

当传递右值时,T被推导为int。(因此,函数参数a的类型为int&&。然后将b声明为从a复制的自变量。

run(value)中,value是一个左值,它需要与T&&匹配。左值不能绑定到右值引用,所以T = intT = int&&不会,就像那时T&& = int&&一样。唯一有效的是T = int&.由于引用折叠,对左值引用的右值引用是左值引用,因此run的实例化如下所示:

template<>
void run<int&>(int &a) {
int &b{a}; // expanding std::forward
++b;
assert(b == a);
assert(&b == &a);
}

显然,断言总是通过。现在,对于run(std::move(value))来说,这个论点确实是一个右值,你得到T = int.然后

template<>
void run<int>(int &&a) {
int b{std::move(a)};
++b;
assert(b == a);
assert(&b == &a);
}

这当然失败了。也许你应该替换

T b{std::forward<T>(a)};

std::decay_t<T> b{std::forward<T>(a)};

这将从T中删除引用(确保b是一个新的(复制/移动的(对象(,并处理数组和函数(即使a不是,也要b成为指针(。

怀疑你需要它们,但[temp.deduct.call]/3谈到了转发引用的模板推导,[dcl.init.list]/3.9说列表初始化引用只是将其绑定到初始值设定项列表的元素。也[forward],好吧,解释std::forward<T>.基本上,如果T是左值引用,则std::forward<T>(x)是左值,否则是x值(一种右值(。(基本上这是一个有条件的std::move

最新更新