2017-12-26 37 views
16

C++ 17に切り替えてカスタムstd::optionalソリューションを標準のものに置き換えると、本当に奇妙で予想外のclang 5の動作が検出されました。何らかの理由でemplace()がパラメータクラスの特性std::is_constructibleの誤った評価のために無効になっていました。clang 5:std ::オプションのインスタンス化ネジstd :: is_constructible特性のパラメータタイプ

それが再現する前にいくつかの特定の前提条件が満たされている必要がありますgodbolt.org


変動に関する

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

ライブ例の前提条件のいずれかと期待どおりにコンパイルします。スタンダードにいくつかの未知の矛盾があり、遵守している間にこのコードを拒否するのですか?

サイドノートとして:GCC 7.1およびGCC 7.2は、上記のコードに問題はありません。


バグレポートで:bugs.llvm.org

+0

コンパイラのバグになる可能性があります。 –

+0

@CrisLuengo、私はそれが標準よりも修正する方が簡単だから願っています。 – GreenScape

+1

あなたの中心は、本当に言語弁護士の質問です。そのように答えられるべきです。 – StoryTeller

答えて

7

これはコンパイラのバグのように見えます。 [class]

からクラスは、クラス指定の閉鎖}で完全に定義されたオブジェクトのタイプ(または完全なタイプ)であると考えられます。 Victimはこの文脈で他のタイプよりも、それは全く違う作りません、std::optional<Victim>で完全であることを意味し

。テンプレートの特殊is_­constructible<T, Args...>のための述語の条件は、次の変数の定義は、いくつかのために整形されるだろう場合に限り、満足しなければならない[meta]

から

は、変数tを発明: T t(declval<Args>()...);

Args...の引数を持つtを直接初期化するか、sizeof...(Args) == 0の場合はの値を初期化します0。

この場合、value-initializing t is to default-initializetであり、したがってstd::is_constructible_v<Victim>が有効である必要があります。

コンパイラはこれをコンパイルするとstruggling a lotと思われます。

+1

同じ段落から: "クラスメンバ仕様の中では、クラスはデフォルトメンバ初期化子(ネストされたクラスのものを含む)内で完全であるとみなされます。これは、コンパイラが '' 'を閉じるまでデフォルトのメンバ初期化子を処理できないことを意味しているようです(入れ子になったクラスの場合は、閉じたクラスの閉じた'} 'が見えるまで)。 – cpplearner

+2

@cpplearner私は、(囲む)クラスが自分の体内で不完全であり、ネストされたクラスが不完全ではないということを信じています。 –

+1

私のポイントは、デフォルトのメンバー初期化子はまだトークンスープです。それらは解析されません)、集約を初期化するには、これらのデフォルトのメンバーイニシャライザを参照する必要があります。これはSFINAEの文脈の外でより明白です: 'struct Outer {struct Inner {int x = 4; }; decltype(Inner())a; }; ' – cpplearner

3

申し訳ありませんが、関連する見積もりを掘り下げてください。問題の要点は、std::is_constructibleVictimを処理する方法です。最も決定的な権限はC++ 17(n4659)です。まず[meta.unary.prop/8]

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

[: is_­constructible<T, Args...>は、変数の定義以下 はいくつかのためによく形成されるだろう場合に限り、満足しなければならないテンプレートの特殊化のための述語条件が 変数tを発明

注: これらのトークンは、関数宣言として決して解釈されません。 - 終了ノート] Tと任意のArgsとは無関係のコンテキストのようにアクセスチェックが実行されます。変数初期化の直後の コンテキストの妥当性のみが考慮されます。

Iが強調表示されたノートは、(音符さのために)規範的ではないが、それは[temp.variadic]/7と一致:

... Nがゼロである場合、膨張のインスタンスは を生成します空リストそのようなインスタンシエーションは、 がリストを完全に省略するか、または文法に曖昧さをもたらす場合であっても、包含構造の構文を変更することはありません。 is_­constructibleの目的のためにそう

、このT t();は確かt変数宣言になります。

その初期括弧の空集合であるオブジェクトは、すなわち、()、 値に初期化しなければならない:[dcl.init/11]は限り言うので、この初期設定値の初期化です。

これは、特性がVictimが値で初期化できるかどうかのチェックを終了することを意味します。それは可能性があります。これは集合体ですが、暗黙的にデフォルトされているデフォルトのc'torはコンパイラによって(値の初期化をサポートするために)明らかに定義されています。

ロングストーリー。 Clangにはバグがあります。報告する必要があります。

関連する問題