2016-02-29 4 views
38

パラメータパックの展開は、VS2015コンパイラによって元に戻されます。異なるC++コンパイラで、パラメータパックの展開が異なるのはなぜですか?

1 
2 
3 
4 

しかし、同じコードの実行で:私はXcodeで、このコード(打ち鳴らす-700.1.81)を実行すると

#include <iostream> 
#include <vector> 


template <typename... T> 
void f_Swallow(T &&...) 
{ 
} 

template <typename... T> 
std::vector<int> f(T ...arg) 
{ 
    std::vector<int> result; 
    f_Swallow 
    (
     [&]() 
     { 

      result.push_back(arg); 
      return true; 
     } 
     ()... 
    ) ; 
    return result; 
} 


using namespace std; 
int main() 
{ 
    auto vec = f(1,2,3,4); 

    for (size_t i = 0; i < vec.size(); ++i) 
     cout << vec[i] << endl; 
} 

が、私はこの結果を得る:

私は、次のコードを持っていますVS2015は次の出力を生成します。

4 
3 
2 
1 

コンパイラによってパラメータパックが異なるのはなぜですか?プラットフォームとコンパイラのバージョンを確認せずに修正する方法はありますか? 標準では展開順序について何も保証されていませんか?

+9

これは不特定の評価順序ではありませんか? (副作用:通常は 'Swallow'イディオム配列のブレースイニシャライザはこの問題を避けるためにのみ使用されます) – milleniumbug

+0

@milleniumbug、あなたは正しいです。そして、あなたはそれを答えとして投稿しなければなりません。 – StoryTeller

+2

これを行う正しい方法は、構築の順序を保証するイニシャライザリストです: '' auto dummy = {(result.push_back(arg)、0)...}; '[Demo](http:// rextester .com/XHIYH83588) –

答えて

46

これは、パラメータパック展開の順序が異なります。これは関数の引数評価の順序です。簡潔にするため

f_Swallow 
(
    [&]() 
    { 

     result.push_back(arg); 
     return true; 
    } 
    ()... 
) ; 

、ちょうどそのラムダNはパラメータ番号で名前funcNを与えることができます。 4つの引数、パラメータパックは、このに任意の準拠コンパイラでに展開されます考える:

f_Swallow(func1(), func2(), func3, func4()) ; 

関数の引数の評価の順序は、C++で指定されていません。コンパイラは、(あなたのバージョンのClangのように)それらを逆順に(あなたのMSVCのバージョンのように)、あるいは好きな順番で評価することができます。評価指図にはカウントできません。

評価の順序が指定されたコンテキストに式を挿入することができます。たとえば:

template <typename... T> 
std::vector<int> f(T ...arg) 
{ 
    std::vector<int> result; 
    (result.push_back(arg), ...); 
    return result; 
} 
11
私はそれはまた、ちょうどこのように書くことができることを考え出し

:C++ 17では

template <typename... T> 
std::vector<int> f(T ...arg) 
{ 
    std::vector<int> result; 
    (void)std::initializer_list<int> { (result.push_back(arg), 0)... }; 
    return result; 
} 

、あなたはfold expressionsで次の操作を行うことができるようになります:

template <typename... T> 
std::vector<int> f(T ...arg) 
{ 
    std::vector<int> result{ arg... }; 
    return result; 
} 

ダミーのstd :: initializer_listを作成する必要はありません

+0

ハッハッハ、そういう場合は、「std :: vector」を作成したい場合に最適ですあなたのパラメータ。 – TartanLlama

+0

興味深いことに、これについては何が役に立つのですか?_vs._ 'vector'自身の' initializer_list'コンストラクタは、とにかく呼びますか?現在、それは私にはテキストの無駄な行であるように見えますが、明らかに何かが不足している可能性があります。 –

+0

私の場合、この関数は多くの場所でこの 'auto vec = f();'のように使用されていたので、関数を修正するのが簡単で、すべての呼び出しを変更しました。 –

関連する問題