次の C++11 コードを検討してください。
#include <iostream>
struct X
{
X(bool arg) { std::cout << arg << '\n'; }
};
int main()
{
double d = 7.0;
X x{d};
}
の初期化で double から bool への縮小変換がありますx
。標準に関する私の理解によれば、これは不正なコードであり、何らかの診断が必要です。
Visual C++ 2013 で次のエラーが発生します。
error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion
ただし、Clang 3.5.0 と GCC 4.9.1 の両方で、次のオプションを使用します。
-Wall -Wextra -std=c++11 -pedantic
エラーや警告なしでこのコードをコンパイルしてください。プログラムを実行すると、1
(驚くことではありません) が出力されます。
では、さらに奇妙な領域に足を踏み入れてみましょう。
に変更X(bool arg)
するX(int arg)
と、突然、Clang からエラーが発生しました
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
そしてGCCからの警告
warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing]
これは私が期待していたもののように見えます。
ここで、bool
コンストラクターの引数を保持し (つまり、 に戻してX(bool arg)
)、 に変更double d = 7.0;
しint d = 7;
ます。繰り返しますが、Clang からの縮小エラーですが、GCC は診断をまったく発行せず、コードをコンパイルします。
定数をコンストラクターに直接渡すと得られる動作のバリエーションがいくつかありますが、奇妙なものもあれば、予想されるものもありますが、ここではそれらをリストしません。この質問は長くなりすぎています。
これは、標準準拠に関して VC++ が正しく、Clang と GCC が間違っているというまれなケースの 1 つだと思いますが、これらのコンパイラのそれぞれの実績を考えると、私はまだこれについて非常に躊躇しています。
専門家はどう思いますか?
標準参照 (C++11 の最終標準ドキュメント、ISO/IEC 14882-2011 からの引用):
8.5.4 [dcl.init.list] パラグラフ 3 には、次のものがあります。
— それ以外の場合、T がクラス型の場合、コンストラクターが考慮されます。適用可能なコンストラクターが列挙され、オーバーロード解決 (13.3、13.3.1.7) によって最適なコンストラクターが選択されます。引数のいずれかを変換するために縮小変換 (以下を参照) が必要な場合、プログラムは不適切な形式です。
同じセクションのパラグラフ 7 には、次のようなものがあります。
縮小変換は、
浮動小数点型から整数型へ、または
long double から double または float へ、または double から float への暗黙的な変換です。ただし、ソースが定数式で、変換後の実際の値が次の場合を除きます。表現できる値の範囲内 (正確に表現できない場合でも)、または
— ソースが定数式であり、変換後の実際の値である場合を除き、整数型またはスコープなし列挙型から浮動小数点型へターゲット型に適合し、元の型に変換されたときに元の値を生成する、または
— ソースが定数式であり、変換後の実際の値がターゲットの型に適合し、元の値を生成する場合を除き、整数型またはスコープなし列挙型から、元の型のすべての値を表すことができない整数型へ元の型に戻すと。
[注: 上で示したように、そのような変換はリストの初期化の最上位では許可されていません。—注の最後]
3.9.1 [basic.fundamental] パラグラフ 7 には、次のものがあります。
型 bool、char、char16_t、char32_t、wchar_t、および符号付きおよび符号なしの整数型をまとめて整数型と呼びます。48 整数型の同義語は整数型です。
(私はこの段階ですべてに疑問を持ち始めていました...)