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