通用参考左值不复制对象
Universal reference l-value not copying object
为什么这些断言适用于以下代码?通用引用应绑定到左值引用 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
}
However both objects addresses "a" and "b" are the same in the run()
function.
传递左值时,T
是deduced as lvalue-reference, i.e. int&
. (int& &&
到int&
,所以函数参数a
的类型是int&
。)然后b
声明为绑定到 a
.
的引用
当传递一个右值时,T
被推断为 int
。 (所以函数参数a
的类型是int&&
。)然后b
声明为从a
复制的自变量。
在run(value)
中,value
是一个左值,需要和T&&
匹配。左值不能绑定到右值引用,所以 T = int
和 T = 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
是一个新的 (copied/moved) 对象)并且还会处理数组和函数(通过使 b
成为指针即使 a
不是)。
怀疑您是否需要它们,但是 [temp.deduct.call]/3
talks about the template deduction of forwarding references, and [dcl.init.list]/3.9
says that list-initializing a reference just binds it to the element of initializer list. Also [forward]
,嗯,解释 std::forward<T>
。基本上,如果 T
是一个左值引用,那么 std::forward<T>(x)
就是一个左值,否则就是一个 xvalue(一种右值)。 (基本上是有条件的std::move
。)
为什么这些断言适用于以下代码?通用引用应绑定到左值引用 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
}
However both objects addresses "a" and "b" are the same in the
run()
function.
传递左值时,T
是deduced as lvalue-reference, i.e. int&
. (int& &&
int&
,所以函数参数a
的类型是int&
。)然后b
声明为绑定到 a
.
当传递一个右值时,T
被推断为 int
。 (所以函数参数a
的类型是int&&
。)然后b
声明为从a
复制的自变量。
在run(value)
中,value
是一个左值,需要和T&&
匹配。左值不能绑定到右值引用,所以 T = int
和 T = 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
是一个新的 (copied/moved) 对象)并且还会处理数组和函数(通过使 b
成为指针即使 a
不是)。
怀疑您是否需要它们,但是 [temp.deduct.call]/3
talks about the template deduction of forwarding references, and [dcl.init.list]/3.9
says that list-initializing a reference just binds it to the element of initializer list. Also [forward]
,嗯,解释 std::forward<T>
。基本上,如果 T
是一个左值引用,那么 std::forward<T>(x)
就是一个左值,否则就是一个 xvalue(一种右值)。 (基本上是有条件的std::move
。)