clang 5:std :: optional instantiation screw std :: is_constructible trait of the parameter type

Gre*_*ape 16 c++ language-lawyer c++17 stdoptional

当切换到c ++ 17并用std::optional标准解决方案替换自定义解决方案时,检测到clang 5的一个非常奇怪和意外的行为.出于某种原因,emplace()由于对std::is_constructible参数类的特征的错误评估而被禁用.

在复制之前必须满足一些特定的前提条件:

#include <optional>

/// Precondition #1: T must be a nested struct
struct Foo
{
    struct Victim
    {
        /// Precondition #2: T must have an aggregate-initializer
        /// for one of its members
        std::size_t value{0};
    };

    /// Precondition #3: std::optional<T> must be instantiated in this scope
    std::optional<Victim> victim;

    bool foo()
    {
        std::optional<Victim> foo;

        // An error
        foo.emplace(); 
        /// Assertion is failed
        static_assert(std::is_constructible<Victim>::value);
    }
};
Run Code Online (Sandbox Code Playgroud)

godbolt.org上的实例


更改任何前提条件并按预期编译.标准中是否存在一些未知的不一致性,使得clang在符合要求时拒绝此代码?

作为旁注:GCC 7.1GCC 7.2对上述代码没有任何问题.


错误报告:bugs.llvm.org

Pas*_* By 7

这看起来像编译器错误.来自[班级]

}在类说明符结束时,类被视为完全定义的对象类型(或完整类型).

这意味着Victim完成std::optional<Victim>,使其与此上下文中的任何其他类型没有区别.

来自[meta]

is_­constructible<T, Args...>当且仅当以下变量定义适用于某些发明变量时,才应满足模板特化的谓词条件t: T t(declval<Args>()...);

哪个是t使用类型的参数进行直接初始化Args...,或者是否sizeof...(Args) == 0是值初始化t.

在这种情况下,value-initializing t是default-initialize t,这是有效的,因此std::is_constructible_v<Victim>应该为true.

说了这么多,编译器似乎是挣扎了很多编写本.

  • @cpplearner我认为这指的是(封闭的)类在它自己的体内是不完整的,而不是它的嵌套类是不完整的 (2认同)