auto
C ++ 11の型が正確さと保守性を向上させる理由がわかります。パフォーマンスを向上させることもできる(私はHerb Sutterによるほぼ常に自動)と読みましたが、良い説明がありません。
auto
パフォーマンスを改善するにはどうすればよいですか?- 誰でも例を挙げられますか?
auto
C ++ 11の型が正確さと保守性を向上させる理由がわかります。パフォーマンスを向上させることもできる(私はHerb Sutterによるほぼ常に自動)と読みましたが、良い説明がありません。
auto
パフォーマンスを改善するにはどうすればよいですか?回答:
auto
暗黙的な暗黙の変換を回避することにより、パフォーマンスを向上させることができます。私が説得力があると思う例は次のとおりです。
std::map<Key, Val> m;
// ...
for (std::pair<Key, Val> const& item : m) {
// do stuff
}
バグを参照してください?ここでは、const参照によってマップ内のすべてのアイテムをエレガントに取得し、意図を明確にするために新しいrange-for式を使用していると考えていますが、実際にはすべての要素をコピーしています。これは、そうでstd::map<Key, Val>::value_type
はstd::pair<const Key, Val>
ないからですstd::pair<Key, Val>
。したがって、(暗黙的に)以下の場合:
std::pair<Key, Val> const& item = *iter;
既存のオブジェクトへの参照を取得してそのままにする代わりに、型変換を行う必要があります。暗黙的な変換が利用可能である限り、別のタイプのオブジェクト(または一時)へのconst参照を使用できます。例:
int const& i = 2.0; // perfectly OK
型変換は、a const Key
をに変換できるのと同じ理由で許可された暗黙の変換ですがKey
、それを可能にするために新しい型の一時を構築する必要があります。したがって、効果的にループは次のようになります。
std::pair<Key, Val> __tmp = *iter; // construct a temporary of the correct type
std::pair<Key, Val> const& item = __tmp; // then, take a reference to it
(もちろん、実際には__tmp
オブジェクトはなく、説明のためだけにあります。実際、名前のない一時ファイルはitem
、その存続期間中のみバインドされます)。
に変更するだけです:
for (auto const& item : m) {
// do stuff
}
大量のコピーを保存しただけです-参照される型は初期化子の型と一致するため、一時的な変換や変換は必要なく、直接参照するだけで済みます。
std::pair<const Key, Val> const &
として処理しようとすることについて不平を言う代わりに喜んでコピーを作成する理由を説明できますstd::pair<Key, Val> const &
か?C ++ 11の新機能ですauto
。どのように範囲を設定し、これを実行するかは不明です。
auto
パフォーマンスを向上させるために使用する理由のすべてをカバーする答えがないということです。なので、以下に自分の言葉で書きます。
auto
パフォーマンスを向上させる」ことの証明だとは思いません。これは、「auto
パフォーマンスを破壊するプログラマーのミスを防ぐのに役立つ」単なる例です。私は、この2つの間に微妙ではあるが重要な違いがあると考えています。それでも、+ 1。
auto
初期化式の型を推定するので、型変換は含まれません。テンプレート化されたアルゴリズムと組み合わせると、これは、特に名前を付けることができないタイプの式を扱う場合に、自分でタイプを作成する場合よりも直接的な計算ができることを意味します。
典型的な例は(ab)usingから来ていstd::function
ます:
std::function<bool(T, T)> cmp1 = std::bind(f, _2, 10, _1); // bad
auto cmp2 = std::bind(f, _2, 10, _1); // good
auto cmp3 = [](T a, T b){ return f(b, 10, a); }; // also good
std::stable_partition(begin(x), end(x), cmp?);
cmp2
andを使用するとcmp3
、アルゴリズム全体で比較呼び出しをインライン化できます。一方、std::function
オブジェクトを作成する場合、呼び出しをインライン化できないだけでなく、関数ラッパーの型消去された内部で多相ルックアップを実行する必要があります。
このテーマのもう1つのバリエーションは、次のように言うことができます。
auto && f = MakeAThing();
これは常に参照であり、関数呼び出し式の値にバインドされ、追加のオブジェクトを構築することはありません。戻り値の型がわからない場合は、のような方法で(おそらく一時的なものとして)新しいオブジェクトを作成する必要がありますT && f = MakeAThing()
。(さらに、auto &&
戻り値の型が可動ではなく、戻り値がprvalueである場合でも機能します。)
auto
です。他のバリアントは「偶発的なコピーを避ける」ですが、装飾が必要です。なぜauto
そこにタイプを入力するよりもスピードが速いのですか?(答えは「型を間違えたら黙って変換する」だと思います)バリーの答えのよく説明されていない例ですよね?つまり、2つの基本的なケースがあります。タイプの消去を回避するautoと、誤って変換されるサイレントタイプのエラーを回避するautoです。どちらも実行時間のコストがあります。
std::bind
、std::function
そしてstd::stable_partition
、すべてのインライン化されていますか?それとも実際には、C ++コンパイラーは混乱を整理するのに十分積極的にインライン化しないでしょうか?
std::function
コンストラクターを通過した後、特に小さな関数の最適化を使用すると、実際の呼び出しを確認するのが非常に複雑になります(したがって、実際には非仮想化は必要ありません)。もちろん、原則としてすべてが
2つのカテゴリがあります。
auto
型の消去を回避できます。名前付けできないタイプ(ラムダなど)と、ほとんど名前付けできないタイプ(結果std::bind
または他の式テンプレートのようなもの)があります。
なしauto
では、データを消去して、次のようなものにする必要がありstd::function
ます。型の消去にはコストがかかります。
std::function<void()> task1 = []{std::cout << "hello";};
auto task2 = []{std::cout << " world\n";};
task1
型消去オーバーヘッド-ヒープ割り当ての可能性、インライン化の難しさ、仮想関数テーブルの呼び出しオーバーヘッド。 task2
なし。ラムダは、型を消去せずに格納するために、自動または他の形式の型の演繹が必要です。他のタイプは非常に複雑で、実際に必要なだけです。
次に、タイプを間違える可能性があります。場合によっては、間違ったタイプは一見完璧に機能しますが、コピーが発生します。
Foo const& f = expression();
がexpression()
返されたBar const&
場合、Bar
または場合によってはBar&
、Foo
から構築できる場合にコンパイルされBar
ます。テンポラリFoo
が作成されてにバインドされf
、そのライフタイムはなくなるまで延長されf
ます。
プログラマーはBar const& f
そこにコピーを作成するつもりだったのではないかもしれませんが、関係なくコピーが作成されます。
最も一般的な例は、のタイプであり*std::map<A,B>::const_iterator
、そうではありstd::pair<A const, B> const&
ませんstd::pair<A,B> const&
が、エラーは、パフォーマンスを暗黙のうちに犠牲にするエラーのカテゴリーです。std::pair<A, B>
からを作成できstd::pair<const A, B>
ます。(マップのキーはconstです。編集は悪い考えです)
@Barryと@KerrekSBの両方が最初に、これらの2つの原則を回答で示しました。これは、2つの問題を1つの回答で強調する試みであり、例を中心とするのではなく、問題を目的とした表現です。
既存の3つの答えは、使用auto
が「意図せずに悲観化する可能性を低くし」、効果的に「パフォーマンスを向上」させる例を示しています。
コインには裏返しがあります。auto
基本オブジェクトを返さない演算子を持つオブジェクトで使用すると、誤った(コンパイル可能で実行可能な)コードになる可能性があります。たとえば、この質問ではauto
、Eigenライブラリを使用して、異なる(誤った)結果がどのように与えられたかを尋ねます。つまり、次の行
const auto resAuto = Ha + Vector3(0.,0.,j * 2.567);
const Vector3 resVector3 = Ha + Vector3(0.,0.,j * 2.567);
std::cout << "resAuto = " << resAuto <<std::endl;
std::cout << "resVector3 = " << resVector3 <<std::endl;
異なる出力が発生しました。確かに、これは主にEigensの遅延評価によるものですが、そのコードは(ライブラリー)ユーザーに対して透過的である必要があります。
ここではパフォーマンスに大きな影響はありませんが、auto
意図しない悲観化を回避するために使用することは、時期尚早の最適化として分類されるか、少なくとも間違っています;)。