求教个神奇的 C++ 打印问题

Betsy · 2024-8-5 23:56:48 · 356 次点击
### 代码

```c++
#include <iostream>
#include <vector>
#include <cstdint>
#include <optional>

using GroupId = std::uint64_t;
using ReducedGroupId = GroupId;

struct Memo {
    std::optional<ReducedGroupId> GetReduceGroupId(const GroupId& group_id) {
        // omit
        return std::make_optional<ReducedGroupId>(group_id);
    }
};

int main(int argc, char* argv[]) {
    std::vector<GroupId> tmp;

    Memo memo;
    GroupId group_id = 1;
    const ReducedGroupId& reduced_group_id = memo.GetReduceGroupId(group_id).value();
    std::cout << "3.1.->|" << reduced_group_id << ":" << &reduced_group_id << std::endl;

    tmp.push_back(4);
    std::cout << "3.2.->|" << reduced_group_id << ":" << &reduced_group_id << std::endl;

    tmp.push_back(5);
    std::cout << "3.3.->|" << reduced_group_id << ":" << &reduced_group_id << std::endl;
    return 0;
}

```

### 结果

```text
3.1.->|1:0x7ffe4fcd3530
3.2.->|4:0x7ffe4fcd3530
3.3.->|5:0x7ffe4fcd3530
```

### 问题

- 3.1, 3.2, 3.3 为啥打印结果不一样?
举报· 356 次点击
登录 注册 站外分享
16 条回复  
zhouxiaoyuan 小成 2024-8-6 01:32:25
不是 c++神奇,是没管理好对象的生命周期,不能引用临时变量 GetReduceGroupId 。

const ReducedGroupId& reduced_group_id = memo.GetReduceGroupId(group_id).value();
Donaldo 小成 2024-8-6 01:40:05
```c++
const ReducedGroupId& reduced_group_id = memo.GetReduceGroupId(group_id).value();
```
华点在这一行,memo.GetReduceGroupId(group_id)是个临时的右值,你取完就悬垂了,所以这个这时候他的值是个 UB 。具体是什么全看编译器实现:
- Apple M2 aarch64+ clang16: 1, 1, 1
- Windows x86 + msvc14: 1, 1, 1
- Linux x86 + gcc14: 1, 4, 5

想要避免 UB ,多加一行把这个临时值存起来就好了
```c++
auto rgi = memo.GetReduceGroupId(group_id);
const ReducedGroupId& reduced_group_id = rgi.value();
```
这里 auto 类型可以是 std::optional<ReducedGroupId>也可以是它的右值引用 std::optional<ReducedGroupId> &&。
chrisyunhua 小成 2024-8-6 02:58:16
去掉 & 改为 `const ReducedGroupId id = ...` 也可以。

@Betsy 不用 optional 没问题,我的理解是:因为 GetReducedGroupId() 属于 prvalue ,赋给 const & 时触发了 lifetime extension ;而 optional.value() 返回类型是 const & 属于 lvalue ,不触发 lifetime extension 。

https://en.cppreference.com/w/cpp/language/lifetime
noahlias 小成 2024-8-6 09:07:31
我怎么觉得是你的编译器问题 或者编译 option 的问题

https://godbolt.org/z/4b6x9WKaE

我看这里返回结果是一样的
```
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
3.1.->|1:0x7ffdce1d3300
3.2.->|1:0x7ffdce1d3300
3.3.->|1:0x7ffdce1d3300
```
blinue 小成 2024-8-6 09:08:39
未定义行为就是编译器怎么做都可以,有一篇很好的博客 https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633
bfjm 小成 2024-8-6 09:20:07
@Betsy 不用 optional 修饰 没问题 应该是提升了返回值的生命周期了(const)
InkStone 小成 2024-8-6 09:38:41
试了一下编译时候有 warning 的……都不用开-Wall

test.cpp:21:46: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]
    const ReducedGroupId& reduced_group_id = memo.GetReduceGroupId(group_id).value();
                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

写 C++的时候-Wall -Werror 是个好习惯
rabbbit 小成 2024-8-6 09:46:46
visual studio 输出的都是 1
这么写行吗?
const ReducedGroupId&& reduced_group_id = std::move(memo.GetReduceGroupId(group_id).value());
fighterhit 小成 2024-8-6 09:56:07
人生苦短,学点别的吧
12下一页
返回顶部