gcc中的std::optional默认构造函数为什么不是constexpr?

5

我有以下代码用于测试我的constexpr可构造的lazy类:

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

我希望它在main中输出10。在clang-8.0中编译时,它按预期运行,但在gcc(无论是8.3还是trunk版本)中编译时,它会导致分段错误。似乎a没有被常量初始化,并且在a初始化之前,在int val = a->f()内部调用了空的a.m_initializer

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

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


这与你的问题有些不相关,但是什么阻止你使用template <typename T> using Lazy = std::optional<T>。我经常采用这种方法来定义一个惰性初始化的变量。 - cplusplusrat
我不想每次使用a的函数时都写懒惰构造if(!a) a.emplace(...);。我希望在第一次使用a时完成一些固定的初始化(通常具有冗长的参数)。我还经常希望对不可移动对象进行一些后期初始化(为简单起见,我已从上面的代码中删除了它)。 - eivour
1
我真的认为这是gcc的一个bug,因为使用m_val的初始化列表也可以解决问题演示 - Jarod42
我并不认为这是一个错误。我不明白编译器如何需要将外部存储的变量进行常量初始化,因为常量初始化需要在编译时发生,而外部变量的实际初始化可能存在于编译时不可见的翻译单元中。 - cplusplusrat
在C++17中,未被捕获的lambda表达式是constexpr,并且明确指出,如果构造函数、所有成员初始化器和所有参数都是constexpr,则对象将被const初始化。 - eivour
显示剩余2条评论
1个回答

1
这是一个关于gcc的bug,还是我漏掉了什么?
是的,可以验证这个bug(程序执行期间的分段错误)在GCC 9.4之前仍然是可重现的,并且从GCC 10.1开始程序可以正常运行(意味着该bug已经被修复)。演示:https://gcc.godbolt.org/z/osWa1no9Y

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接