C++ clang5:std::可选实例化std::是参数类型的可构造特征

C++ clang5:std::可选实例化std::是参数类型的可构造特征,c++,language-lawyer,c++17,stdoptional,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

当切换到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);
    }
};
#包括
///前提条件#1:T必须是嵌套结构
结构Foo
{
结构受害者
{
///前提条件#2:T必须具有聚合初始值设定项
///为其成员之一
std::size\u t值{0};
};
///前提条件#3:std::optional必须在此范围内实例化
性病:选择性受害者;
布尔福()
{
std::可选foo;
//错误
foo.emplace();
///断言失败
静态断言(std::is_constructible::value);
}
};

现场示范


更改任何先决条件,它将按预期编译。标准中是否存在一些未知的不一致性,使得clang在遵守时拒绝此代码

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



错误报告位于:

这看起来像是一个编译器错误。从

类在类说明符的结尾处被视为完全定义的对象类型(或完全类型)

这意味着
受害者
std::optional
处完成,与此上下文中的任何其他类型都没有区别

当且仅当以下变量定义对于某个发明变量
t
格式良好时,应满足模板专用化的谓词条件
是可构造的:
T(declval
T
,它是有效的,因此
std::is\u constructible\u v
应该是真的


尽管如此,编译器似乎正在编译这篇文章。

好吧,找到相关的引文。问题的关键是
std::is_constructible
应该如何处理
受害者。最具决定性的权威是C++17(n4659)。首先:

模板专门化的谓词条件 当且仅当 对于某些人来说,下面的变量定义形式良好 变量t:

T t(declval<Args>()...);
T(十二月:

…当N为零时,扩展的实例化将生成 空列表。这样的实例化不会改变语法 对封闭结构的解释,即使在 如果完全忽略该列表,则会导致格式错误或 造成语法上的歧义

因此,为了使
是可构造的
,此
T();
确实使
T
成为一个变量声明。此初始化是值初始化,因为:

初始值设定项为空括号集的对象,即(), 应初始化该值

这意味着trait最终会检查
受害者
是否可以初始化值。它可能是一个聚合,但隐式默认的默认c'tor仍然由编译器定义(显然是为了支持值初始化)


长话短说。Clang有一个bug,你应该报告它。

很可能是一个编译器bug。@CrisLuengo,我希望如此,因为它比标准更容易修复。实际上,你的核心是一个语言律师问题。应该这样回答。检查。这是所涉及特性的一个简单实现,在这方面应该都是正确的s context.gcc 7.2有最详细的错误消息“解析'Foo::Victor::value'的非静态数据成员之前需要构造函数”,似乎任何默认初始值设定项与可选成员的组合都嵌套在结构中打破了这一点。`=0`而不是
value
上的{0}也失败。同一段落:“在类成员规范中,类在[…]默认成员初始值设定项中被视为完整的(包括嵌套类中的这类内容)。”这似乎意味着编译器在看到结束
}
(对于嵌套类,直到它们看到封闭类的结尾。
)@cppleener我认为这是指(封闭)类在其自身主体内不完整的事实,而不是其嵌套类不完整。我的观点是,默认成员初始值设定项仍然是token soup(即,它们未被解析)在封闭类中,初始化聚合将需要引用这些默认成员初始值设定项。这在SFINAE上下文之外更为明显:
struct Outer{struct interner{int x=4;};decltype(Inner())a;}
@cppleerner噢,那真是太糟糕了。虽然我找不到什么明确规定它是非法的,但它看起来确实应该是非法的