2015-09-11 25 views
10

初期化リストとconst autoを組み合わせたときのC++ 11の正しい動作を理解しようとしています。私は、次のコードをGCCとクラン間で異なる振る舞いを取得していますし、正しいものであるかを知るしたいと思います:const auto std :: initializer_list ClangとGCCの違い

グラムでコンパイル
#include <iostream> 
#include <typeinfo> 
#include <vector> 

int main() 
{ 
    const std::initializer_list<int> l1 = { 1, 2, 3 }; 
    const auto l2 = { 1, 2, 3 }; 

    std::cout << "explicit: " << typeid(l1).name() << std::endl; 
    std::cout << "auto:  " << typeid(l2).name() << std::endl; 
} 

++出力は、次のとおりです。打ち鳴らす++コンパイルされたバージョンながら

explicit: St16initializer_listIiE 
auto:  St16initializer_listIKiE 

生産:

explicit: St16initializer_listIiE 
auto:  St16initializer_listIiE 

クランがを生成しながら、GCCはstd::initializer_list<const int>autoラインを回しているようです。 GCCバージョンでは、std::vectorの初期化に使用すると問題が発生します。したがって、以下はClangで動作しますが、GCCのコンパイラエラーが発生します。 GCCは正しいバージョンを生産している場合

// Compiles under clang but fails for GCC because l4 
std::vector<int> v2 { l2 }; 

は、様々なSTLコンテナは、これらのケースのために別のリスト初期化子の過負荷を含むように拡張されなければならないことを示唆しているようです。

注:この動作は、GCC(4.8,4.9,5.2)およびClang(3.4および3.6)の複数のバージョン間で一貫しているようです。

+0

'std :: vector v2(l2.begin()、l2.end());'をチェックすることができます(これはうまくいくはずです)暗黙的に変換する任意の型のイニシャライザリストは、控除が変更された場合。一方、これにより、イニシャライザリストのコンストラクタが他のコンストラクタをより頻繁に隠す可能性があります。 –

+0

はい 'std :: vector v2(l2.begin()、l2.end());確かに動作します。 – daveraja

答えて

6

GCCバグ。 (N4527を引用)dcl.spec.auto]/P7:

変数を推定戻り型または可変型が タイプから決定される[...] 、プレースホルダタイプが初期化され使用して宣言する場合そのイニシャライザの[...]そうでない場合は、Tを変数[...]の宣言された型 とします。プレースホルダーがauto タイプ指定子の場合、推論タイプはテンプレート引数の控除のルールを使用して決定されます。初期化が ダイレクトリスト初期化 [...]の場合。 [...]それ以外の場合は、新たに考案 型テンプレートパラメータUのいずれかでautoの発生を置き換えることにより、TからPを取得したり、初期化がstd::initializer_list<U>コピーリスト初期化、ある場合。 関数呼び出し(14.8.2.1)からのテンプレート引数の控除のルールを使用してUの値を引いてください。ここで、Pは関数テンプレートパラメータ の型であり、対応する引数は初期化子[...]です。 控除が失敗した場合、宣言は不正です。そうでない場合は、 をPに代入して を代入することによって、変数または戻り値の型として導出された型 が得られます。したがって、const auto l2 = { 1, 2, 3 };に、控除は、関数テンプレート呼び出しmeow({1, 2, 3})所与

template<class U> void meow(const std::initializer_list<U>); 

ためかのように実行される

ここで、const-lessのケースauto l3 = { 1, 2, 3 };(GCCは正しくstd::initializer_list<int>と推測されます)を考えてみましょう。この場合の控除は、関数テンプレートの場合と同様に実行されます。purr({1, 2, 3})を指定して、関数テンプレートの場合と同様に控除を実行します。

関数パラメータの最上位のcv修飾は無視されるので、2つの控除で同じ型が得られるはずです。


[temp.deduct。コール]/P1:

テンプレート引数控除は、後述のように、呼の 対応する引数の型を持つ各機能 テンプレートパラメータタイプ(Pそれを呼び出す)(Aそれを呼び出す)と比較することによって行われます。その後、 [...] Pが依存型の場合、 Pから参照およびCV-修飾子を削除すると、いくつかのP'のために[...] std::initializer_list<P'>を与え、 引数が空でない初期化子リスト(8.5.4)で、P'を関数テンプレートのパラメータタイプとし、初期化子要素 を引数として、イニシャライザリストの各要素の代わりに を実行します。 12、または3に対して(Uある)P'を推定

、タイプintのすべてのリテラルは、明らかintをもたらします。

+2

私はトップレベルのcv-qualification *がここでどのように問題になっているか分かりません。問題は 'initializer_list 'であり、 'const initializer_list 'ではありません。 –

+0

' auto l3 = {1,2,3} gccで同じテンプレートパラメータ( 'const int')を推論しないでください。 –

+0

'auto l3 = {1,2,3};を修正しても、gccの' const auto l2 = {1,2,3}; 'と同じテンプレートパラメータを推論しません。試してみると、 'typeid(l3).name()'は 'St16initializer_listIiE'を生成します。 – daveraja

4

あり、これと似たようなケースについてはgccのバグレポートwrong auto deduction from braced-init-listで、リチャード・スミス氏は、それはgccのバグであることを示します:

でもシンプルなテストケース:decltype(r)const std::initializer_list<const int>と推定されるので、

#include <initializer_list> 
const auto r = { 1, 2, 3 }; 
using X = decltype(r); 
using X = const std::initializer_list<int>; 

は失敗しますconst std::initializer_list<int>ではなく

変数はプレースホルダ型を使用して宣言し、初期化され、またはreturn文:

ドラフトCの部分は++標準は言うセクション7.1.6.4[dcl.spec.auto]だろうプレースホルダー型を含む戻り型で宣言された関数 で発生した場合、推定戻り型または変数型 は、その初期化子の型から決定されます。 [...]関数の変数または返される型の宣言された型をTとします。 プレースホルダが自動型指定子である場合、テンプレート引数 控除のルールを使用して演繹型が決定されます。 [...]そうでない場合は、autoの出現を の新しい発明型のテンプレートパラメータUに置き換えるか、または初期化子がbraced-init-listの場合はstd :: initializer_- のリストでPを取得します。ファンクションコール(14.8.2.1)、 からのテンプレート引数の控除のルールを使用してUの値を引きます。ここで、Pは関数テンプレートのパラメータタイプであり、イニシャライザは対応する引数です[...] [例:

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int> 
auto x2 = { 1, 2.0 }; // error: cannot deduce element type 

-end例] [例:

const auto &i = expr; 

私のタイプは、以下の発明関数テンプレートのコールF(expr)は、パラメータUの推定タイプであります:

template <class U> void f(const U& u); 

末端例]

+0

クイックレスポンスとバグレポートへのリンクをありがとう。 – daveraja

関連する問題