Rokiのチラ裏

学生による学習のログ

Default constructor improperly invoked in C++1z mode in GCC 7.1.0

※5/16追記
私の推測では、C++14とC++17間で、[expr.type.conv]の文面に差異がある*1という事実の基の意見でしたが、[expr.type.conv]に該当しない場合でも発生する事を確認したため、議論がややこしくならないよう、内容を全て取り下げ、タイトルを変更しました。

※5/17追記
有志によるstack overflowへの質問の結果、GCCのバグではないかという推測の元バグレポートが送信されました。執筆時(JST 2017/5/18 15:37)現在のStatusはUNCONFIRMED

タイムラインに流れてきた話題。

上記コードの上から3つめのmy_vector({})の挙動について。

つまり、C++14と17ではそれぞれ解釈が異なるという事になる。それぞれ14はn3690、17はn4660を参照している。該当する文面は以下の通り。まずは n3690 [expr.type.conv]/(2) からの引用。

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, whose value is that produced by value-initializing (8.5) an object of type T; no initialization is done for the void() case. [ Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are discarded when determining the type of the resulting prvalue (Clause 5). — end note ]

次に、n4660 [expr.type.conv]/(2) からの引用。

If the initializer is a parenthesized single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (8.4). If the type is cv void and the initializer is (), the expression is a prvalue of the specified type that performs no initialization. Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized (11.6) with the initializer. For an expression of the form T(), T shall not be an array type.

n3690 [expr.type.conv]/(2)では、配列でない完全なオブジェクト型または(場合によってはcv修飾された)void型の simple-type-specifier または typename-specifier である場合、指定された型のprvalueを作成する。それは、タイプTのオブジェクトをvalue-initializing(n3690 [dcl.init])することによって生成されるというように示されている。
一方、n4660 [expr.type.conv]/(2) では、型がcv voidで、初期化子が()の場合、式は初期化を実行しない指定された型のprvalueである。そうでない場合、式による結果のオブジェクトは、direct-initialized (n4660 [dcl.init.list]/(3.4))によって初期化された型のprvalueである。形式がT()のような表現の場合、Tは配列型であってはならないと示されている。
今回の挙動の差に直接該当する文面に下線を引いた。つまり、上記ツイートのコードで言えば、C++14ではmyvector({})での{}initializer_list<int>{}として解釈されるためmyvector(std::initializer_list<int>)が、17ではdirect-initialized(n4660 [dcl.init.list]/(3.4))によって初期化されたprvalue、つまりmyvector()となるため、default ctorが呼びされる。

*1:value-initialization、direct-initializationへの進路と解釈に差異がある