何时以及为什么在初始化引用时获取副本?
When and why do I get a copy when initializing a reference?
有些情况下,我想要一个对象的引用,但却得到了一个副本。
这是一个例子:
std::pair<const std::string, int> foo("hello", 5);
const std::pair<std::string, int> & bar = foo;
std::cout << "foo: " << foo.first << " " << foo.second << std::endl;
std::cout << "bar: " << bar.first << " " << bar.second << std::endl;
foo.second = 7;
std::cout << "foo: " << foo.first << " " << foo.second << std::endl;
std::cout << "bar: " << bar.first << " " << bar.second << std::endl;
这会产生:
foo: hello 5
bar: hello 5
foo: hello 7
bar: hello 5
所以显然 foo
的副本已经创建,而语法表明(至少对我而言)程序员想要引用它。
这违反了引用应该是某物的别名的原则。如果有人能解释发生了什么以及为什么,那就太好了。
(注意:我遇到了这个here)
foo
和 bar
的底层类型不同,因此使用从 RHS 上的类型到 LHS 上的类型的隐式转换创建临时文件* 。 C++ 标准允许 const
引用绑定到临时对象并延长其生命周期。
const
引用 bar
绑定到那个临时对象,它是一个不同于 foo
的对象。
如果您使用相同的类型,您会得到预期的结果:
std::pair<const std::string, int> foo("hello", 5);
const std::pair<const std::string, int> & bar = foo;
或
std::pair<std::string, int> foo("hello", 5);
const std::pair<std::string, int> & bar = foo;
会产生
foo: hello 5
bar: hello 5
foo: hello 7
bar: hello 7
*std::pair
有一个 template constructor 允许从一种类型的对到另一种类型的隐式转换。
这是对 const
的特殊 属性 引用(自然是右值引用)。这些引用可以绑定到临时对象。
注意 std::pair<const std::string, int>
(foo
的类型)与 std::pair<std::string, int>
(bar
想要引用的类型,模 const
).您的代码中没有 std::pair<std::string, int>
类型的对象,因此 bar
无法绑定到任何此类对象。
然而,正如我所说,对 const
的引用和右值引用可以绑定到临时对象。 std::pair<std::string, int>
类型的对象可以从 std::pair<const std::string, int>
类型的对象隐式创建。因此,创建了这样一个临时对象,并将 bar
绑定到该临时对象。此引用绑定还将临时对象的生命周期延长至 bar
*.
的生命周期
这就是您获得副本的原因。如果将 bar
的类型更改为 std::pair<std::string, int> &
(即删除 const
),则会出现编译错误,即非常量左值引用无法绑定到临时变量。
* 在引用类型的成员变量绑定到临时变量的特殊情况下,临时变量的生命周期只会延长到初始化引用的构造函数结束。
有些情况下,我想要一个对象的引用,但却得到了一个副本。 这是一个例子:
std::pair<const std::string, int> foo("hello", 5);
const std::pair<std::string, int> & bar = foo;
std::cout << "foo: " << foo.first << " " << foo.second << std::endl;
std::cout << "bar: " << bar.first << " " << bar.second << std::endl;
foo.second = 7;
std::cout << "foo: " << foo.first << " " << foo.second << std::endl;
std::cout << "bar: " << bar.first << " " << bar.second << std::endl;
这会产生:
foo: hello 5
bar: hello 5
foo: hello 7
bar: hello 5
所以显然 foo
的副本已经创建,而语法表明(至少对我而言)程序员想要引用它。
这违反了引用应该是某物的别名的原则。如果有人能解释发生了什么以及为什么,那就太好了。
(注意:我遇到了这个here)
foo
和 bar
的底层类型不同,因此使用从 RHS 上的类型到 LHS 上的类型的隐式转换创建临时文件* 。 C++ 标准允许 const
引用绑定到临时对象并延长其生命周期。
const
引用 bar
绑定到那个临时对象,它是一个不同于 foo
的对象。
如果您使用相同的类型,您会得到预期的结果:
std::pair<const std::string, int> foo("hello", 5);
const std::pair<const std::string, int> & bar = foo;
或
std::pair<std::string, int> foo("hello", 5);
const std::pair<std::string, int> & bar = foo;
会产生
foo: hello 5
bar: hello 5
foo: hello 7
bar: hello 7
*std::pair
有一个 template constructor 允许从一种类型的对到另一种类型的隐式转换。
这是对 const
的特殊 属性 引用(自然是右值引用)。这些引用可以绑定到临时对象。
注意 std::pair<const std::string, int>
(foo
的类型)与 std::pair<std::string, int>
(bar
想要引用的类型,模 const
).您的代码中没有 std::pair<std::string, int>
类型的对象,因此 bar
无法绑定到任何此类对象。
然而,正如我所说,对 const
的引用和右值引用可以绑定到临时对象。 std::pair<std::string, int>
类型的对象可以从 std::pair<const std::string, int>
类型的对象隐式创建。因此,创建了这样一个临时对象,并将 bar
绑定到该临时对象。此引用绑定还将临时对象的生命周期延长至 bar
*.
这就是您获得副本的原因。如果将 bar
的类型更改为 std::pair<std::string, int> &
(即删除 const
),则会出现编译错误,即非常量左值引用无法绑定到临时变量。
* 在引用类型的成员变量绑定到临时变量的特殊情况下,临时变量的生命周期只会延长到初始化引用的构造函数结束。