左值与右值可疑

时间:2015-08-20 13:35:11

标签: c++ c++11 move lvalue rvalue

以下代码运行良好,但据我所知,它不应该

#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创建一个名为Data的{​​{1}}实例。然后创建一个名为data的{​​{1}}实例,该实例存储对Foo的引用。然后将该istance复制到向量foo内。因此,当函数结束时,data应包含悬空引用,因为vec被销毁。我对吗?所以我希望获得一些调用方法vec[0]的垃圾。是偶然的,我获得了正确的价值1还是我错过了什么?

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

修改构造函数
data

的功能
print()

通过这种方式,explicit Foo(Data&& data):data_(data){} 及其Foo foo(std::move(data)); 内的副本包含实例foo,而不是对它的引用。我对吗?这是正确的解决方案吗?这样,vec[0]必须是data类型或Foo::data_类型?

3 个答案:

答案 0 :(得分:2)

如您所知,由于悬空参考,您的示例代码具有未定义的行为。你看到的行为只是偶然。

一个采用左值参考的函数表示&#34;我将从你传入的任何内容中窃取数据&#34;。让构造函数接受这样的引用是好的,只要这些是你的语义,但它似乎不是你的例子的情况。

有可能采用参数by-value,然后将其移入成员变量:

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

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

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

答案 1 :(得分:2)

是的Foo将持有一个悬空参考。 Foo类应该保存Data not Data&amp;或数据&amp;&amp;。

#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();
}

答案 2 :(得分:1)

你是对的,这是偶然的,这实际上属于未定义的行为 字段应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();
}