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

时间:2017-12-26 06:48:24

标签: 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);
    }
};

godbolt.org

上的实例

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

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

错误报告位于:bugs.llvm.org

2 个答案:

答案 0 :(得分:7)

这看起来像编译器错误。来自[class]

  

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

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

来自[meta]

  

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

使用t类型的参数直接初始化Args...,或者如果sizeof...(Args) == 0,则初始化t

在这种情况下,value-initializing t is to default-initialize t有效,因此std::is_constructible_v<Victim>应为真。

尽管如此,编译器似乎正在编译struggling a lot

答案 1 :(得分:3)

好的,挖出相关的报价。问题的关键是std::is_constructible应该如何处理Victim。最具决定性的权威是C ++ 17(n4659)。首先[meta.unary.prop/8]

  

模板特化的谓词条件   当且仅当,is_­constructible<T, Args...>才会得到满足   对于一些发明的人来说,下面的变量定义会很好   变量t:

T t(declval<Args>()...);
     

[注意:这些令牌永远不会被解释为函数声明。    - 结束注释]访问检查就像在上下文中一样执行   与T和任何Args无关。只有立即的有效性   考虑变量初始化的上下文。

我强调的注释不是规范性的(因为是注释),但它与[temp.variadic]/7重合:

  

...当N为零时,扩展的实例化产生一个   空列表。这样的实例化不会改变句法   对封闭结构的解释,即使在其中   完全省略该列表否则将是不正确的或将会   导致语法含糊不清。

因此,出于is_­constructible的目的,此T t();确实使t成为变量声明。这个初始化是值初始化,因为[dcl.init/11]说的很多:

  

一个对象,其初始化程序是一组空的括号,即(),   应进行价值初始化。

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

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