2

変数への参照を使用してブースト融合ベクトルを作成したいと考えています。目標は、さまざまな数のパラメーターを関数に渡し、それらを融合ベクトルに追加することです。参照型のため、すべての要素を TMP で一度に 1 つずつ追加します。ただし、融合ベクトルの一部の要素が間違っている場合があります。未定義の動作 (間違った値、読み取りアクセス違反) のようです。
理解を容易にするために、TMP で使用される再帰を「展開」した例を書きました。Fusion ベクトルに 2 つの参照を追加するだけで、結果が出力されます。

#include <iostream>

#include <boost/ref.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container.hpp>

using namespace boost;
using namespace boost::fusion;

//add second element
template <typename T>
vector<int&, double&> createVector2(T vec, double& v2) {
     auto newVector = join(vec, make_vector(ref(v2)));
     return newVector;
}

//add first element
template <typename T>
vector<int&, double&> createVector(T vec, int& v1, double& v2) {
     auto newVector = join(vec, make_vector(ref(v1)));
     return createVector2(newVector, v2); 
}

int main() {
     int v1 = 10;
     double v2 = 15.3;

     vector<> vec;
     auto ret = createVector(vec, v1, v2);

     std::cout << at_c<0>(ret) << std::endl;
     std::cout << at_c<1>(ret) << std::endl;

     if (at_c<0>(ret) != v1) {
         std::cout << "FAILED" << std::endl;
     }

     if (at_c<1>(ret) != v2) {
         std::cout << "FAILED" << std::endl;
     }

     return 0;
}

ブースト融合ベクトルの参照がアクセスされると (読み取りアクセス違反)、最初に次の行でプログラムがクラッシュします。

std::cout << at_c<0>(ret) << std::endl;

コンパイラとして VC11 を使用しています (バージョン 17.00.51106.1)。エラーはリリース モードでのみ発生します。しかし、VC10、GCC 4.7.0、または GCC 4.7.2 を使用すると、エラーは発生せず、プログラムは完全に正常に動作します。
プログラムを VC11 で動作させるには、この行を変更する必要があります

auto newVector = join(vec, make_vector(ref(v1)));

auto newVector = as_vector(join(vec, make_vector(ref(v1))));

では、上記の例にはバグが含まれているのでしょうか、それとも VC11 オプティマイザーに何か問題があるのでしょうか? ローカル ブースト フュージョン ビュー (boost::fusion::join はビューのみを返し、ビューは boost::fusion::vector で「通常の」boost::fusion::vector に変換されます) を別のビューに渡すことはできますか?値で機能しますか?

4

1 に答える 1

1

Microsoft connect (リンク) でバグ レポートを提出しました。私の質問に対する答えは、私のコードにバグがあるということです。

問題は、join 関数が seq1 と seq2 の 2 つの要素を含むブースト融合の joint_view を返すことです。それらは次のように定義されます。

template <typename Sequence1, typename Sequence2>
struct joint_view : sequence_base<joint_view<Sequence1, Sequence2> >
{
    (...)
private:
    typename mpl::if_<traits::is_view<Sequence1>, Sequence1, Sequence1&>::type seq1;
    typename mpl::if_<traits::is_view<Sequence2>, Sequence2, Sequence2&>::type seq2;
};

私のコードの問題は、(make_vector から返された) 一時オブジェクトを結合関数に渡すことです。ブースト融合ベクトルはビューではなく、seq1 と seq2 は参照です。そのため、join 関数は一時オブジェクトへの参照を含む joint_view を返しますが、これは無効です。これを解決するには、次の 2 つの変更があります。

  1. 最初の解決策 (createVector と同じ):

    template <typename T>
    vector<int&, double&> createVector2(T vec, double& v2) {
        auto x = make_vector(ref(v2));
        auto newVector = join(vec, x);
        return newVector;
    }
    

    join は有効な x への参照を含む joint_view を返すようになりました。最後に、ビューはブースト融合ベクトルに変換され、参照が解決されます。

  2. 2 番目の解決策 (createVector と同じ):

    template <typename T>
    vector<int&, double&> createVector2(T vec, double& v2) {
        return join(vec, make_vector(ref(v2)));
    }
    

    一時オブジェクト (make_vector によって返される) の有効期間は return ステートメント全体であり、最初のバージョンと同様に、ビューはブースト フュージョン ベクターに変換され、参照は再び解決されます。

これら 2 つのソリューションを提供してくれた Eric Brumer (Microsoft) に感謝します。

結論: 一時オブジェクトをブースト フュージョン結合関数に渡さないでください (別のビューの場合のみ)。

于 2013-04-18T07:01:11.603 に答える