可选的嵌套类和 is_constructible 之间奇怪的交互

Pao*_*o M 4 c++ inner-classes type-traits c++17

在处理现实项目时,我偶然发现了某些(某些版本)编译器的奇怪行为。考虑以下类声明:

struct OptionalsStruct {
    struct InnerType {
        bool b{};
    };

    OptionalsStruct() = default;

    std::optional<InnerType> oInnerType;
};
Run Code Online (Sandbox Code Playgroud)

对于某些编译器,它OptionalStruct::InnerType不可构造的,但不可构造默认可构造的clang 1116GCC 10),对于其他一些编译器,它既不是不可构造的clang 910),更不用说clang 8如何看待整体事物。

我的问题是:这些行为是编译器错误,还是标准中的漏洞(我使用的是 C++17)?我在这里错过了什么吗?

use*_*522 8

Inner直到}最外层封闭类之后,编译器才认为“完全完成”,因为完整类上下文规则要求编译器延迟在默认成员初始值设定项中进行查找b,直到该点之后,但 ( 的某些属性封闭类的特殊成员函数的隐式声明可能间接依赖于默认成员初始值设定项的属性。

通过这种解释,您可以std::optional使用不完整的类型作为模板参数进行实例化,从而获得未定义的行为。

实际上, 的实例化std::optional可能会导致您在 中使用的类型特征特化的实例化main,具体取决于std::optional实现。如果是这样,则类型特征特化会使用不完整的类型进行实例化,这也会导致未定义的行为,并且实际上无法产生合理的结果。

由于类模板特化仅实例化一次(并且编译器可以缓存结果),因此您以后对特征的使用取决于先前隐式实例化的无意义结果。

据我所知,嵌套类的完整性没有得到很好的规定,这是标准中的一个公开缺陷,并且不清楚编译器的解释是否是“正确的”。

(从非常迂腐的角度来看,无论如何,它都是符合标准的 UB,因为 的实例化点将std::optional<InnerType>位于导致隐式实例化的命名空间范围声明之前,即 before struct OptionalsStruct {,其中InnerType肯定不完整。请参阅CWG 问题 287。)