何时以及为什么在初始化引用时获取副本?

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

foobar 的底层类型不同,因此使用从 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),则会出现编译错误,即非常量左值引用无法绑定到临时变量。


* 在引用类型的成员变量绑定到临时变量的特殊情况下,临时变量的生命周期只会延长到初始化引用的构造函数结束。