0

次の C++ 関数があるとします。

// Returns a set containing {1!, 2!, ..., n!}.
set<int> GetFactorials(int n) {
  set<int> ret;
  int curr = 1;
  for (int i = 1; i < n; i++) {
    curr *= i;
    ret.insert(curr);
  }

  return ret;
}

set<int> fs = GetFactorials(5);

(これはダミーの例です。重要なのは、関数がセット自体を作成し、それを返すことです。)

私の友人の 1 人は、私が行ったように関数を書く代わりに、戻り時にセットをコピーするのを避けるために、関数がセットへのポインターを受け取るように書くべきだと言っています。彼は次のような意味だったと思います:

void GetFactorials2(int n, set<int>* fs) {
  int curr = 1;
  for (int i = 1; i < n; i++) {
    curr *= i;
    fs->insert(curr);
  }
}

set<int> fs;
GetFactorials2(5, &fs);

私の質問: この 2 番目の方法は本当に大きな利点ですか? 私にはかなり奇妙に思えます。私は C++ を初めて使用し、コンパイラについてはあまり知りませんが、コンパイラの魔法を使えば、元の関数はそれほど高価にはならないと思います。(そして、自分でセットを初期化する必要がなくなります。)間違っていますか?これを理解するには、ポインタとコピー オン リターンについて何を知っておくべきですか?

4

2 に答える 2

3

いいえ、一般的にはまったく有利ではありません。最近のほぼすべての合理的なコンパイラは、名前付きの戻り値の最適化を利用しています (こちらを参照)。これにより、前の例からのパフォーマンスの低下が効果的に取り除かれます。

本当に核心に迫りたい場合は、Dave Abrahams によるこの記事をお読みください(後押しの大きな貢献者の 1 人です)。要するに、値を返すだけです。それはおそらく速いです。

于 2013-04-15T04:39:59.050 に答える
0

はい、それは高価になる可能性があります。特にセットが大きくなると。ここでポインタや参照を使用しない理由はありません。それはあなたを大いに節約し、読みやすさに関して多くを犠牲にすることはありません.

また、自分で最適化できるのに、コンパイラの最適化に頼る必要はありません。コンパイラはコードを認識していますが、常にアルゴリズムを認識しているわけではありません。

私はこれをするだろう

void GetFactorials2(int n, set<int>& fs) {
//                                   ^^
  int curr = 1;
  for (int i = 1; i < n; i++) {
    curr *= i;
    fs->insert(curr);
  }
}

通話は通常のままです。

set<int> fs;
GetFactorials2(5, fs);
                  ^^ 
于 2013-04-15T04:36:54.060 に答える