左值与右值可疑
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
创建了一个名为 data
的 Data
实例。然后创建一个 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();
}
下面的代码 运行 很好,但据我所知,它不应该
#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
的 Data
实例。然后创建一个 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();
}