0

完全な質問ではありませんが、スタイルによってそのようなコードをよりエレガントに記述し、同時に新しい c++ 標準などを十分に活用する方法について熟考していたものです。例を次に示します。

フィボナッチ数列を最大 N 個の値のコンテナーに戻します (数学的に傾いていないものについては、これは前の 2 つの値を加算し、最初の 2 つの値が 1 に等しくなるようにします。つまり、1,1,2,3,5,8,13, .. .)

メインから実行する例:

std::vector<double> vec;
running_fibonacci_seq(vec,30000000);

1)

template <typename T, typename INT_TYPE>
    void running_fibonacci_seq(T& coll, const INT_TYPE& N)
    {
        coll.resize(N);
        coll[0] = 1;
        if (N>1) {
        coll[1] = 1;
        for (auto pos = coll.begin()+2;
            pos != coll.end();
            ++pos)
        {
            *pos = *(pos-1) + *(pos-2);
        }
        }
    }

2) 同じですが、& 1.e の代わりに右辺値&& を使用します。

void running_fibonacci_seq(T&& coll, const INT_TYPE& N)

編集:以下にコメントしたユーザーが気づいたように、右辺値と左辺値はタイミングに何の役割も果たしません-コメントで説明されている理由により、速度は実際には同じでした

N = 30,000,000 の結果

Time taken for &:919.053ms

Time taken for &&: 800.046ms

まず、これは実際には問題ではないことを知っていますが、これらのどれまたはどれが最新の C++ コードとして最適ですか? 右辺値参照 (&&) を使用すると、ムーブ セマンティクスが適切に配置され、不要なコピーが作成されないように見えます。これにより、時間が少し改善されます (将来のリアルタイム アプリケーション開発のため、私にとって重要です)。いくつかの特定の「質問」は

a) コンテナー (私の例では vector でした) をパラメーターとして関数に渡すことは、右辺値が実際にどのように使用されるべきかについての洗練された解決策ではありません。この事実は本当ですか?もしそうなら、右辺値は上記の例でそれが軽いことを実際にどのように示しますか?

b) coll.resize(N); 呼び出しとN=1の場合、これらの呼び出しを回避する方法はありますか?そのため、ユーザーはベクトルのサイズを動的に作成せずに関数のみを使用する単純なインターフェイスを使用できます。コンパイル時にベクトルが特定のサイズで割り当てられるように、テンプレートのメタプログラミングをここで使用できますか? (ie running_fibonacci_seq<30000000>) 数値が大きくなる可能性があるため、テンプレート メタプログラミングを使用する必要がある場合は、これ (リンク)も使用できます。

c) もっと洗練された方法はありますか? std ::transform 関数は、ラムダを使用して使用できると感じています。

    void running_fibonacci_seq(T&& coll, const INT_TYPE& N)
    {
        coll.resize(N);
        coll[0] = 1;
        coll[1] = 1;
        std::transform (coll.begin()+2,
                coll.end(),         // source
                coll.begin(),       // destination
                [????](????) {      // lambda as function object
                    return ????????;
                });
    }

[1] http://cpptruths.blogspot.co.uk/2011/07/want-speed-use-constexpr-meta.html

4

3 に答える 3

2

「参照の折りたたみ」のため、このコードは右辺値参照を使用せず、何も移動しません。

template <typename T, typename INT_TYPE>
void running_fibonacci_seq(T&& coll, const INT_TYPE& N);

running_fibonacci_seq(vec,30000000);

これを認識すると、すべての質問(および既存のコメント)はまったく意味がなくなります。

于 2013-01-04T19:24:00.217 に答える
2

明白な答え:

std::vector<double> running_fibonacci_seq(uint32_t N);

なんで ?

const-ness のため:

std::vector<double> const result = running_fibonacci_seq(....);

簡単な不変式のため:

void running_fibonacci_seq(std::vector<double>& t, uint32_t N) {
    // Oh, forgot to clear "t"!
    t.push_back(1);
    ...
}

しかし、速度はどうですか?

戻り値の最適化と呼ばれる最適化があり、多くの場合、コンパイラがコピーを省略 (および呼び出し元の変数に直接結果を構築) できるようにします。コピー/移動コンストラクターに副作用がある場合でも、C++ 標準によって明確に許可されています。

では、なぜ「出力」パラメータを渡すのでしょうか?

  • 戻り値は 1 つしかありません (ため息)
  • t割り当てられたリソース (ここでは のメモリ バッファ)を再利用したい場合があります。
于 2013-01-04T19:07:53.767 に答える
1

これをプロファイリングします:

#include <vector>
#include <cstddef>
#include <type_traits>

template <typename Container>
Container generate_fibbonacci_sequence(std::size_t N)
{
    Container coll;
    coll.resize(N);
    coll[0] = 1;
    if (N>1) {
      coll[1] = 1;
      for (auto pos = coll.begin()+2;
        pos != coll.end();
        ++pos)
      {
        *pos = *(pos-1) + *(pos-2);
      }
    }
    return coll;
}

struct fibbo_maker {
  std::size_t N;
  fibbo_maker(std::size_t n):N(n) {}
  template<typename Container>
  operator Container() const {
    typedef typename std::remove_reference<Container>::type NRContainer;
    typedef typename std::decay<NRContainer>::type VContainer;
    return generate_fibbonacci_sequence<VContainer>(N);
  }
};

fibbo_maker make_fibbonacci_sequence( std::size_t N ) {
  return fibbo_maker(N);
}

int main() {
  std::vector<double> tmp = make_fibbonacci_sequence(30000000);
}

物事はfibbo_maker私が賢いだけです。しかし、それを繰り返さなくても、必要な fibbo シーケンスのタイプを推測できます。

于 2013-01-04T19:17:40.453 に答える