C++ 来自std::pair rvalues构造函数的垃圾值
编辑底部的C++ 来自std::pair rvalues构造函数的垃圾值,c++,constructor,std-pair,rvalue,C++,Constructor,Std Pair,Rvalue,编辑底部的 我发现pair的构造函数有一个行为,我不完全理解 所以我尝试用右值初始化一对,代码如下: pair<vector<int> &&, int> a([]() -> vector<int> { vector<int> b{1}; cout << &b << ' ' << b[0] << '\n'; return b; }(), 0);
我发现
pair
的构造函数有一个行为,我不完全理解
所以我尝试用右值初始化一对,代码如下:
pair<vector<int> &&, int> a([]() -> vector<int> {
vector<int> b{1};
cout << &b << ' ' << b[0] << '\n';
return b;
}(), 0);
cout << &a.first << ' ' << a.first[0] << '\n';
显然,a.first
是垃圾
然后我在网上找到了pair
的构造函数:
pair (const first_type& a, const second_type& b);
template<class U, class V> pair (U&& a, V&& b);
但是现在a.first
的地址与b
的地址不同:
0x62fdf0 1
0x62fdc0 1
但是,如果我删除外部无用的对
,代码将正常工作(即相同的地址和值)。为什么?我如何才能使对
工作
编辑 在尝试理解@cigien的注释后,我将旧代码大量缩减为
pair<int &&, int> a(1, 2);
cout << a.first << '\n';
警告已被此代码删除。但这段代码的输出是:
2
0
这真的给我留下了一点线索。非常感谢您的帮助。您正在创建临时对象,对它们进行(右值)引用,然后在基础对象消失后尝试使用这些引用
在第一个示例中,[]()->vector{…}()
是类型为vector
的prvalue(生成对象的说明)表达式。对
构造函数需要对真实对象的引用,而不是生成对象的指令,因此在调用构造函数之前,会调用lambda并创建临时的向量
。然后构造函数将对该对象的引用存储到对中
,然后在构造函数完成并将控件返回给您后销毁该对象。因此,该对的第一个元素现在是垃圾
在第二种情况下,构造函数获取对临时向量的引用(并且以相同的方式创建临时),但是这一次,它不是存储引用(即,从向量和构造向量和),而是从引用构造向量。从向量&
构造向量
由向量
的构造函数的某个重载处理(该重载具有特殊名称“移动构造函数”)。此构造函数获取临时对象内堆分配的句柄,并简单地将所有权授予临时对象对内的对象。现在,一旦临时数据被销毁,实际数据仍然存在,但句柄(即向量
对象)本身位于不同的位置
最后一个示例与第一个示例存在相同的问题。当var
被初始化时,0
(一个int
prvalue)并不引用一个真实的对象,所以你不能只绑定一个引用var.first
0
被具体化为一个临时对象,var。首先使
引用该对象,然后临时对象被销毁var.first
现在处于悬空状态,您无法对其执行任何操作,因此代码的其余部分是UB,在“引擎盖下”找出哪里出了问题没有多大意义(尽管它看起来确实很有趣)。事实上,从这样一个临时变量初始化引用成员是毫无用处的,以至于从缺陷报告1696开始,var
的初始化是非法的(应该是一个“硬”编译错误)。(但旧的编译器可能会接受它,甚至当前的编译器也可能(错误地)将其降级为警告。)
现在,如果您真的想在对
中“直接”构造向量
,而不调用其移动构造函数,您可以这样做,即创建一个对象,该对象将暂停向量
的构造,直到构建对
的字段
template<typename F>
struct initializing {
F self;
initializing(F self) : self(std::move(self)) { }
operator decltype(auto)() { return self(); }
};
这也消除了从lambda中具体化对象的移动。您正在创建临时对象,对它们进行(右值)引用,然后在基础对象消失后尝试使用这些引用
在第一个示例中,[]()->vector{…}()
是类型为vector
的prvalue(生成对象的说明)表达式。对
构造函数需要对真实对象的引用,而不是生成对象的指令,因此在调用构造函数之前,会调用lambda并创建临时的向量
。然后构造函数将对该对象的引用存储到对中
,然后在构造函数完成并将控件返回给您后销毁该对象。因此,该对的第一个元素现在是垃圾
在第二种情况下,构造函数获取对临时向量的引用(并且以相同的方式创建临时),但是这一次,它不是存储引用(即,从向量和构造向量和),而是从引用构造向量。从向量&
构造向量
由向量
的构造函数的某个重载处理(该重载具有特殊名称“移动构造函数”)。此构造函数获取临时对象内堆分配的句柄,并简单地将所有权授予临时对象对内的对象。现在,一旦临时数据被销毁,实际数据仍然存在,但句柄(即向量
对象)本身位于不同的位置
最后一个示例与第一个示例存在相同的问题。当var
被初始化时,0
(一个int
prvalue)并不引用一个真实的对象,所以你不能只绑定一个引用var.first
0
被具体化为一个临时对象,var。首先使
引用该对象,然后临时对象被销毁var.first
现在处于悬空状态,您无法对其执行任何操作,因此代码的其余部分是UB,在“引擎盖下”找出哪里出了问题没有多大意义(尽管它看起来确实很重要)
struct s_t {
int && first = 0;
} var;
int main() {
var.first = 2;
cout << var.first << '\n';
cout << var.first << '\n';
}
2
0
template<typename F>
struct initializing {
F self;
initializing(F self) : self(std::move(self)) { }
operator decltype(auto)() { return self(); }
};
pair<vector<int>, int> a(
initializing([]() -> vector<int> {
vector<int> b{1};
cout << &b << ' ' << b[0] << '\n';
return b;
}), 0);
template<typename F>
struct initializing {
F &&self;
initializing(F &&self) : self(std::forward<F>(self)) { }
operator decltype(auto)() { return std::forward<F>(self)(); }
};