1

しばらく前に、コンパイラが何らかの作業を行い、次のような式を単純化できるようにするコードを読んだことを覚えています。

// edit: yes the parameters where meant to be passed by reference
//       and maintain constness sorry !
template< typename T >
std::vector<T> operator+( const std::vector<T>& a, const std::vector<T>& b )
{
    assert( a.size() == b.size() );
    std::vector<T> o; o.reserve( a.size() );
    for( std::vector<T>::size_type i = 0; i < a.size(); ++i )
        o[i] = a[i] + b[i];
    return o;
}

// same for operator* but a[i] * b[i] instead

std::vector<double> a, b, c, d, e;

// do some initialization of the vectors

e = a * b + c * d

通常、新しいベクトルが作成され、各演算子に割り当てられますが、代わりに、コンパイラは代わりに 1 つのコピーのみを作成し、それにすべての操作を実行します。

このテクニックは何ですか?

4

3 に答える 3

3

@Agnew が非常に早い段階で言及したように、あなたが説明している手法は式テンプレートです。

これは通常、 ではなくベクトル1std::vectorの数学的概念で行われます。大まかなストロークは次のとおりです。

  1. ベクトルに対する数学演算で結果が返されないようにしてください。代わりに、最終的に実行する必要がある操作を表す プロキシ オブジェクトを返すようにします。a * b乗算する必要がある 2 つのベクトルへの const 参照を保持するだけの「乗算プロキシ」オブジェクトを返すことができます。

  2. これらのプロキシの数学演算も記述して、それらを連鎖できるようにします。したがって、すべての数学を実行したり、ベクトルをコピーしたりすることなく、にa * b + c * dなり(TempMulProxy) + (TempMulProxy)ます。(TempAddProxy)

  3. 右側のオブジェクトのプロキシ オブジェクトを受け取る代入演算子を記述します。このオペレーターは式全体を見てa * b + c * d、目的地を知りながら、ベクトルでその操作を効率的に行うことができます。複数の一時的なベクター オブジェクトを作成する必要はありません。

1または行列または四元数など...*

于 2013-02-07T16:52:05.517 に答える
1

あなたが望むものは、「C++ プログラミング言語」にあります。22.4.7 一時、コピー、およびループ [num.matrix] のBjarne Stroustrup による第 3 版。本を読むことは常に良い考えです。

お持ちでない場合は、基本的に次の 2 つのオプションがあります。

最初に、最も予想される組み合わせのいくつかを直接計算するための一連の関数を作成し (たとえば、mul_add_and_assign(&U,&M,&V,&W) で U =M*V+W を計算します)、ユーザーにどの関数を選択するかを指示します。彼は最も便利です。

2 番目:各操作の引数への参照のみを保持し、 への演算子変換を定義するいくつかの補助クラス (たとえばVxV、など) を導入できます。ここで、参照によって 2 つのベクトルを取り、対応する型のオブジェクトを返すだけの演算子andのオーバーロードを作成します。より複雑な操作を計算するタイプのクラスを作成できます。これで、 aへの assにオーバーロードできます。そして、この最後のオーバーロードでは、補助クラス オブジェクトに保持されている引数への参照を使用してすべての計算を行い、一時的なベクトルを作成しないか最小限に抑えました。VplusVvector+*VxVplusVxVoperator=VxVplusVxVvector

于 2013-02-07T17:56:34.693 に答える
1

ここに質問はありません。しかし、私の水晶玉は、 、、、a * b + c * dが同じサイズのベクトル ( ) であるようなベクトルに対してコンポーネントごとの算術演算を実行するために思いついた 2 つの方法のうち、より良い方法を知りたいと言っています。abcdstd::vector<T>

  1. 実行する操作ごとに、要素をループし、計算を実行して、結果のベクトルを返します。これらの操作をベクトルの式にまとめます。

  2. 入力ベクトルの各要素について、式全体を計算し、それを 1 つの最終結果ベクトルに書き込みます。

考慮すべき点が 2 つあります。

  • パフォーマンス: ここでは、プロセッサが不要な一時ベクトルを割り当てないため、2 番目のオプションが優先されます。
  • 再利用性: 明らかに、ベクトルのアルゴリズム操作を実装し、ベクトルで目的の式を表現するだけでそれらを再利用できると便利です。

ただし、非常にきれいに見える 2 番目のオプションを実装するための優れたオプションがあります。

std::vector<int> a, b, c, d, e;
// fill a, b, c, d with data

auto expression = [](int a, int b, int c, int d){ return a * b + c * d; };

assert (a.size() == b.size() && b.size() == c.size() && c.size() == d.size());
e.reserve(a.size());

for(auto _a = a.begin(), _b = b.begin(), _c = c.begin(), _d = d.begin(), _e = e.begin();
    _a != a.end();
    ++_a, ++_b, ++_c, ++_d, ++_e)
{
    *_e = expression(*_a, *_b, *_c, *_d);
}

このようにして、式をロジックから分離して評価できます。

void componentWise4(std::function<int(int,int,int,int)> f,
                    const std::vector<int> & a,
                    const std::vector<int> & b,
                    const std::vector<int> & c,
                    const std::vector<int> & d,
                    std::vector<int> & result)
{
    assert (a.size() == b.size() && b.size() == c.size() && c.size() == d.size());
    result.reserve(a.size());

    for(auto _a = a.begin(), _b = b.begin(), _c = c.begin(), _d = d.begin(), _result = result.begin();
        _a != a.end();
        ++_a, ++_b, ++_c, ++_d, ++_result)
    {
        *_result = expression(*_a, *_b, *_c, *_d);
    }
}

次に、次のように呼び出されます。

std::vector<int> a, b, c, d, e;
// fill a, b, c, d with data

componentWise4([](int a, int b, int c, int d){ return a * b + c * d; },
               a, b, c, d, e);

この「式エバリュエーター」は、C++ 11 の新機能「可変個引数テンプレート」を使用して拡張し、式内の任意の数の引数だけでなく、異なる型もサポートできると確信しています。私はそれを機能させることができませんでした (可変個引数テンプレートのこと)、ここで私の試みを完了することができます: http://ideone.com/w88kuG (私は可変個引数テンプレートに慣れていないので、構文)。

于 2013-02-07T16:19:23.163 に答える