左值与右值可疑

lvalue vs rvalue dubious

下面的代码 运行 很好,但据我所知,它不应该

#include <iostream>
#include <vector>

struct Data
{
  explicit Data():value(1){}
  int value;
};

struct Foo
{
  explicit Foo(Data& data):data_(data){}

  inline void print() const
  {
    std::cout<<data_.value<<std::endl;
  }
  Data& data_;
};

void addEntry(std::vector<Foo>& vec)
{
  Data data;
  Foo foo(data);
  vec.push_back(foo);
}

int main()
{
  std::vector<Foo> vec;
  addEntry(vec);
  vec[0].print();
}

函数 addEnty 创建了一个名为 dataData 实例。然后创建一个 Foo 的实例,称为 foo,它存储对 data 的引用。然后将该距离复制到向量 vec 中。因此,当函数结束时,vec[0] 应该包含一个悬挂引用,因为 data 被销毁了。我对吗?所以我希望获得一些垃圾调用方法 print()。是我偶然获得了正确的值 1 还是我遗漏了什么?

为了使其正确,我会移动数据以避免悬空引用。所以我会用

修改构造函数
explicit Foo(Data&& data):data_(data){}

的函数
Foo foo(std::move(data));

以这种方式 foo,因此它在 vec[0] 中的副本包含实例 data 而不是对它的引用。我对吗?这是正确的解决方案吗?这样,Foo::data_需要是Data类型或者Data&?

类型

你是对的,这是偶然的,这实际上属于未定义行为。
字段应 Data 输入 Foo 以避免悬空引用。
你可以这样改写:

#include <iostream>
#include <vector>

struct Data
{
  explicit Data():value(1){}
  int value;
};

struct Foo
{
  explicit Foo(Data&& data):data_(std::move(data)){}

  inline void print() const
  {
    std::cout<<data_.value<<std::endl;
  }
  Data data_;
};

void addEntry(std::vector<Foo>& vec)
{
  vec.emplace_back(Foo(Data()));
}

int main()
{
  std::vector<Foo> vec;
  addEntry(vec);
  vec[0].print();
}

正如您所建议的,由于悬空引用,您的示例代码具有未定义的行为。您看到的行为纯属偶然。

采用右值引用的函数表示 "I'm going to steal data from whatever you pass in"。只要这些是您的语义,让构造函数采用这样的引用就可以了,但您的示例似乎并非如此。

一种可能性是按值获取参数,然后将其移动到成员变量中:

struct Foo
{
  explicit Foo(Data data):data_(std::move(data)){}
  Data data_;
};

这样,客户端代码可以传递左值(1 个副本,1 次移动)或右值(2 次移动)。只维护一个构造函数很方便,但如果 Data 移动起来很昂贵,这可能效率低下。

其他可能性是让单个构造函数采用转发引用,或者为右值维护一个重载,为左值维护一个重载。

是的,Foo 将持有悬空引用。 Foo class 应该保存 Data 而不是 Data& 或 Data&&。

#include <iostream>
#include <vector>

struct Data
{
  explicit Data():value(1){}
  int value;
};

struct Foo
{
  // this is needed if you want to pass lvalue
  Foo(const Data& data):data_(data) 
  {}
  // for rvalue
  Foo(Data&& data):data_(std::move(data))
  {}

  void print() const
  {
    std::cout<<data_.value<<std::endl;
  }

  Data data_;
};

void addEntry(std::vector<Foo>& vec)
{
  vec.emplace_back(Foo(Data())); 

  // or

  Data data;
  // do somth with data
  vec.emplace_back(Foo(std::move(data)));        

  // or

  Data data;
  // do somth with data
  Foo foo {std::move(data)};
  // do somth with foo, but
  // do not use data here!!!
  vec.push_back(std::move(foo));        
}

int main()
{
  std::vector<Foo> vec;
  addEntry(vec);
  vec[0].print();
}