为什么循环访问直接通过 std::optional<T>::value() 访问的容器不起作用?

Why doesn't iterate over a container accessed directly through std::optional<T>::value() work?

提问人:w128 提问时间:6/30/2023 最后编辑:JeJow128 更新时间:7/1/2023 访问量:108

问:

我正在尝试遍历我通过 .与我的预期相反,如果我首先将其存储到副本中,则行为会有所不同,如果我直接迭代它:std::vector<X>struct Tstd::optional<T>std::optional<T>

#include <optional>
#include <string>
#include <vector>

// The rest is the same in both implementations    
struct Object
{
    std::string id;
    std::vector<SomeStructType> items;
};

Object readDataFile() { /*....*/ return {}; }
bool dataFileExists() { /*....*/ return true; }

std::optional<Object> getData()
{
    if (!dataFileExists()) {
        return std::nullopt;
    }

    Object obj = readDataFile();
    return obj;
}

int main()
{
    // Implementation 1 - works
    auto items = getData().value().items;
    for (auto const& item : items)
    {
        // will normally iterate over items
    }

    // Implementation 2 - for loop is skipped
    for (auto const& item : getData().value().items)
    {
        // this body will never execute
    }
}

我希望这两个人的行为是一样的。为什么实现 2 跳过 for 循环?`

在这两种情况下,读取的文件是相同的,有 .我在提供的代码中省略了一些其他检查和断言,但我不认为它们是相关的。items.size() == 1nullopt

C++ 算法 C++20 std可选 基于范围的循环

评论

2赞 Marek R 6/30/2023
代码始终存在于某个上下文中。应始终在函数中提供代码以提供此类上下文。在这种情况下,您应该提供最小的可重现示例
3赞 pptaszni 6/30/2023
stackoverflow.com/questions/37635908/......

答:

5赞 JeJo 6/30/2023 #1

希望这两个人的行为是一样的。为什么实现 2 跳过 for 循环?

在第二个实现中,返回一个临时的表达式,它将被销毁表达式的末尾(换句话说,甚至在基于范围的 for 循环开始之前)。getData()std::optional<Object>

for (auto const &item : getData().value().items)
//                      ^^^^^^^^^ ---> temporary std::optional<Object>
{
}

因此,基于范围的 for 循环包含无效 .使用此方法会导致程序的未定义行为。在您的情况下,循环未执行。items


话虽如此,如果打开了编译器警告,您早就收到了警告。例如,clang 15 with 说:-Wall -Wextra -pedantic-errors -fsanitize=address,undefined

<source>:25:29: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]
    for (auto const& item : getData().value().items)
                            ^~~~~~~~~
1 warning generated.
ASM generation compiler returned: 0
<source>:25:29: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]
    for (auto const& item : getData().value().items)
                            ^~~~~~~~~
//...
=================================================================
==1==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7f1262400040 at pc 0x55ba03d4ae30 bp 0x7ffd89ca2a90 sp 0x7ffd89ca2a88
//...


您可以通过一些调整来延长使用寿命。请参阅本文中提到的示例。

评论

1赞 w128 6/30/2023
谢谢。我正在使用 MSVC,它的代码分析没有指出这一点......我猜最好切换到叮当整齐!