4

我有以下代码来测试我的 constexpr 可构造惰性类:

https://godbolt.org/z/rMLCiL

#include <optional>

template <class T>
class Lazy
{

    using initializer_t = T (*)();
    std::optional<T> m_val = std::nullopt;
    initializer_t m_initializer;

public:
    constexpr Lazy(initializer_t initializer = initializer_t{[] { return T{}; }}) noexcept
        : m_initializer{initializer} {}

    T& operator*()
    {
        if (!m_val.has_value()) {
            m_val = m_initializer();
        }
        return *m_val;
    }
    constexpr T* operator->() { return &(**this); }
};


#include <iostream>
struct A {
    int f() { return 10; }
    ~A()
    {
        std::cout << "Goodbye A " << (void*)this << std::endl;
    }
};
extern Lazy<A> a;

int val = a->f();

Lazy<A> a{[] { return A{}; }};

int main()
{
    std::cout << val << std::endl;
}

我希望它打印 10 in main. 在 clang-8.0 中编译时,它按预期运行,但在 gcc 中编译时(在 8.3 中或在主干中),它会导致分段错误。似乎a不是constant-initialized,它在初始化之前在a.m_initializer内部调用 null 。int val = a->f()a

Cppreference表示std::optional<T>可以使用constexpr构造函数将其初始化为 std::nullopt ,无论 T 是否可轻易破坏。因此,Lazy<A> a{[] { return A{}; }}应该在初始化之前进行常量int val = a->f();初始化。如果我注释掉A::~A,即使使用 gcc 编译,它也会按预期运行。这是 gcc 中的错误,还是我遗漏了什么?

更新:我还发现如果我创建std::optional<T>一个基类而不是拥有这样的成员,它在 gcc 中可以正常工作。另外,如果我只是将行更改std::optional<T> m_val = std::nullopt;std::optional<T> m_val;,它可以正常工作(std::optional<T> m_val{};不起作用)。我真的不明白。

4

1 回答 1

0

这是 gcc 中的错误,还是我遗漏了什么?

是的,可以验证这个错误(程序执行期间的分段错误)在 GCC 9.4 之前仍然可以重现,并且程序从 GCC 10.1 开始运行良好(这意味着错误已修复)。演示:https ://gcc.godbolt.org/z/osWa1no9Y

于 2022-01-22T10:45:32.527 回答