我试图创建一个类,它从两个成员变量示例化一个std::pair<const T&, const U&>
。我开始这个过程是出于好奇,并且惊讶地看到创建多个Pair
对象的结果,在下面的例子中。
struct Pair {
int num;
string s;
pair<const int&, const string&> pr{make_pair(num, s)};
Pair(int n, const string& s) : num(n), s(s) {}
};
ostream& operator<<(ostream& out, const Pair& p) {
out << setw(2) << p.num << ", " << p.s << ": <" << p.pr.first << "," << p.pr.second << ">";
return out;
}
/******************************************************************************/
int main() {
cout << string(15, '-') << " PIECEMEAL " << string(15, '-') << endl;
Pair p1{1, "normal1"};
Pair p2{2, "normal2"};
cout << p1 << endl;
cout << p2 << endl << endl;
cout << string(15, '-') << " VECTOR " << string(15, '-') << endl;
vector<Pair> v;
for (int i=0; i< 05; ++i) {
v.emplace_back(i, "from_vec'" + ::to_string(i) + "'");
}
for(const auto & p : v) {
cout << p << endl;
}
}
我原以为pair
中的const
引用类型声明会分别引用Pair
成员num
和s
。看起来pair
中的引用最终引用了(在某种程度上)最近构造的Pair
的Pair
成员。
下面是输出:
--------------- PIECEMEAL ---------------
1, normal1: <2,normal2>
2, normal2: <1586964272,normal2>
--------------- VECTOR ---------------
0, from_vec'0': <4,from_vec'4'>
1, from_vec'1': <1586964272,from_vec'4'>
2, from_vec'2': <1586964272,from_vec'4'>
3, from_vec'3': <1586964272,from_vec'4'>
4, from_vec'4': <1586964272,from_vec'4'>
我查看了cppreference,试图了解发生了什么,但仍然没有领会到,为什么pair
中的引用不再引用Pair
成员num
和s
?
2条答案
按热度按时间qv7cva1a1#
您的代码有两个问题。
第一:
std::make_pair(num, s)
首先创建一个临时pair<int, string>
,然后用这个临时pair
初始化pr
。请注意,
pair<int, string>
与pari<const int&, const string&>
不同,这意味着它不会触发默认的移动构造函数。相反,如果您查看cppreference,这将触发第6个重载,并且实际上用这个临时的
pair
来分配pr.first
和pr.second
,从而产生悬空引用。要修复此问题,您应该使用以下命令专门创建
pair
引用:或者直接使用
num
和s
创建pr
:第二:
这一行将触发默认的move构造函数,因为您没有自己指定一个,它将在
Pair
上执行成员级移动。但是,当您将
pr
移动到新的Pair
时会发生什么?旧的pr
引用旧的num
和旧的s
。将旧的pr
移动到新的Pair
不会更改pr
的值,因此它将继续引用旧的num
和旧的s
。从而产生悬空引用。因此,您必须手动定义它们,而不是依赖默认生成的移动构造函数:
注意,你不需要在初始化器列表中构造
pr
,因为你已经有了pr
的默认初始化器。Demo
kfgdxczn2#
您无意中创建了一个对,它包含对另一个对的常量引用,而该对在堆栈上分配并立即释放。换句话说,这是未定义的行为。
下面是示例化Pair时所发生的事情:
1.调用Pair构造函数。
1.调用Pair初始化式。
1.初始化器调用make_pair,在堆栈上创建一对int和std::string。
1.这个临时对被赋值给pr,它里面的const引用现在引用临时对的内存。
1.初始化器退出,释放堆栈上的临时对。pr内部的int和string引用现在引用已被释放的内存。
此外,出于内存安全的考虑,你真的不应该把对其他成员的引用存储在你的结构体中,如果你想要一个std::对你的两个值,你应该创建一个成员函数来返回它,而不是把它存储在结构体本身中。