C ++ 11で廃止されたC ++イディオムはどれですか?


192

新しい標準では、物事を行うための新しい方法があり、多くは古い方法よりも優れていますが、古い方法はまだ問題ありません。また、下位互換性の理由から、新しい標準が公式にあまり非推奨にならないことも明らかです。したがって、残っている問題は次のとおりです。

C ++ 11スタイルよりも確実に劣るコーディングの古い方法と、代わりに今できることは何ですか?

これに答えるには、「自動変数を使用する」などの明らかなことをスキップできます。


13
イディオムを廃止することはできませ
Pubby

6
Going Native 2012でのHerb Sutterの講演はこれをカバーしました:
bames53

5
定数値を返すことは推奨されなくなりました。明らかauto_ptrに非推奨です。
Kerrek SB、2012

27
もちろんできます、Pubby。C ++テンプレートが発明される前は、テンプレートを実行するマクロ手法がありました。次にC ++がそれらを追加し、古い方法は悪いと考えられていました。
アランBaljeu

7
この質問は、Programmers.seに移動する必要があります。
Nicol Bolas

回答:


173
  1. 最終クラス:C ++ 11は、finalクラスの派生を防ぐための指定子を提供します
  2. C ++ 11ラムダは、名前付き関数オブジェクト(ファンクター)クラスの必要性を大幅に減らします。
  3. 移動コンストラクターstd::auto_ptr右辺値参照のファーストクラスサポートにより、動作が不必要になる魔法の方法。
  4. Safe bool:これについては前述しました。C ++ 11の明示的な演算子は、この非常に一般的なC ++ 03イディオムを取り除きます。
  5. 縮小して合わせる:多くのC ++ 11 STLコンテナーは、shrink_to_fit()メンバー関数を提供します。これにより、一時関数と交換する必要がなくなります。
  6. 一時的な基本クラス:一部の古いC ++ライブラリは、このかなり複雑なイディオムを使用しています。移動のセマンティクスでは、これはもう必要ありません。
  7. タイプセーフ列挙列挙は、C ++ 11では非常に安全です。
  8. ヒープ割り当ての禁止= delete構文は、特定の機能が明示的に拒否されていることを示すより直接的な方法です。これは、ヒープ割り当ての防止(つまり、=deleteメンバーの場合operator new)、コピーの防止、割り当てなどに適用できます。
  9. テンプレート化されたtypedef:C ++ 11のエイリアステンプレートは、単純なテンプレート化されたtypedefの必要性を減らします。ただし、複合型ジェネレータには依然としてメタ関数が必要です。
  10. フィボナッチなどの一部のコンパイル時の数値計算は、一般化された定数式を使用して簡単に置き換えることができます
  11. result_of:クラステンプレートの使用はresult_ofに置き換える必要がありdecltypeます。入手可能な場合にresult_of使用decltypeすると思います。
  12. クラス内のメンバー初期化子は、デフォルト値を持つ非静的メンバーのデフォルト初期化のためのタイピングを節約します。
  13. 新しいC ++ 11では、コードNULLをとして再定義する必要nullptrがありますが、STLの話を見て、なぜそれを反対したのかを学んでください。
  14. 式テンプレートの狂信者は、C ++ 11に後続の戻り型関数構文があることを喜ばしく思います。30行の長い改行タイプはもうありません!

やめようと思います!


詳細なものをありがとう!
アランバルジュー

7
正解ですがresult_of、リストから削除します。そのtypename前に必要な面倒なことにもかかわらず、私typename result_of<F(Args...)::typeは時々読みやすくなると思いますdecltype(std::declval<F>()(std::declval<Args>()...)、そしてN3436がワーキングペーパーに受け入れられて、彼らは両方ともSFINAEのために働きました(それは以前は提供decltyperesult_ofなかった利点でした)
ジョナサンウェイクリー

...関数本体用と一回一回decltype()for文-私はまだ私は二度同じコードを書くためにマクロを使用する必要が泣いてる)14について

2
このトピックは、このMicrosoftページからC ++言語の概要の「詳細情報」の記事としてリンクされていますが、このトピックは非常に専門的なトピックです。私は簡単にすることを提案してもよい「このトピックでは、C ++初心者のためではありません!」トピックまたはこの回答の冒頭にアドバイスを含めますか?
Aacini、2015

Re 12:「クラス内メンバーの初期化」-これは新しいイディオムであり、非推奨のイディオムではありませんか。文の順序を切り替えますか?Re 2:ファンクタは、オブジェクトではなくタイプ(特にテンプレートパラメータ)を渡す場合に非常に役立ちます。したがって、非推奨となっているのはファンクタの一部の使用法のみです。
einpoklum 2016年

66

ある時点で、const値だけではなく値で戻る必要があると主張されました。

const A foo();
^^^^^

これはC ++ 98/03ではほとんど無害であり、次のようないくつかのバグをキャッチしている可能性もあります。

foo() = a;

しかしconst、C ++ 11では、移動のセマンティクスが阻害されるため、by by は禁忌です。

A a = foo();  // foo will copy into a instead of move into it

だからリラックスしてコーディングしてください:

A foo();  // return by non-const value

9
ただし、関数に参照修飾子を使用することで、回避可能なミスを検出できるようになりました。上記の場合のように定義するA& operator=(A o)&代わりにA& operator=(A o)。これらは愚かな間違いを防ぎ、クラスを基本的な型のように振る舞わせ、移動のセマンティクスを妨げません。
Joe

61

を放棄0してNULLを支持するようになったらすぐにnullptr、そうしてください!

非一般的なコードでは、0またはの使用NULLはそれほど重要ではありません。しかし、汎用コードでnullポインター定数を渡し始めるとすぐに、状況は急速に変化します。0a template<class T> func(T) Tに渡すとint、nullポインタ定数ではなく、として推定されます。そして、それをその後NULLポインター定数に戻すことはできません。これは、宇宙が使用されているだけでは存在しない問題の泥沼にカスケードしnullptrます。

C ++ 11は非推奨0ではなくNULL、nullポインター定数として使用します。ただし、コーディングしたようにコーディングする必要があります。


decltype(nullptr)とは何ですか?

4
@GrapschKnutsch:ですstd::nullptr_t
ハワードHinnant 14

イディオムを採用する新条約ではなく、非推奨として提案する、これは言い換えること(例えば「の使用0またはNULLヌルポインタのために」)。
einpoklum 2016年

38

安全なブールイディオムexplicit operator bool()

プライベートコピーコンストラクター(boost :: noncopyable)→ X(const X&) = delete

プライベートデストラクタと仮想継承を使用した最終クラスのシミュレーションclass X final


良い簡潔な例で、その中に「イディオム」という言葉さえ含まれています。よくできました
セバスチャンマッハ

2
うわー、「安全なブールイディオム」は今まで見たことがありません。C ++ 11以前のコードではそれが必要にならないことを願っています...
ボイシー

24

C ++ 11で基本的なアルゴリズムを作成することを避ける理由の1つは、標準ライブラリによって提供されるアルゴリズムと組み合わせたラムダの可用性です。

私は今それらを使用していますが、いまいましいループを再度書く必要がなく、count_if()、for_each()、または他のアルゴリズムを使用して、やりたいことをどれだけ頻繁に伝えるかは驚くべきことです。

完全なC ++ 11標準ライブラリでC ++ 11コンパイラを使用すると、 使用すると、標準のアルゴリズムを使用してを構築しないという言い訳はもうありません。ラムダはただそれを殺します。

どうして?

実際には(このアルゴリズムの記述方法を自分で使用した後)、意味を知るために暗号化を解除する必要があるいくつかのループよりも、何が行われるかを意味する単純な単語で構築されたものを読む方がはるかに簡単に感じられます。とはいえ、ラムダ引数を自動的に推定することは、構文を生のループと比較しやすくするのに役立ちます。

基本的に、標準的なアルゴリズムで作成されたアルゴリズムの読み取りは、ループの実装の詳細を隠す単語としてはるかに簡単です。

構築する低レベルのアルゴリズムがあるので、今考えなければならないのはより高レベルのアルゴリズムだけだと思います。


8
実際には良い言い訳があります。あなたはBoost.Rangeのアルゴリズムを使用していますが、それははるかに優れています;)
Nicol Bolas

10
for_eachラムダを使用することは、ループ内のラムダの内容を使用して、同等の範囲ベースのforループよりも優れていることはわかりません。コードは多かれ少なかれ同じに見えますが、ラムダはいくつかの余分な句読点を導入します。同等のものを使用しboost::irangeて、明らかに反復子を使用するループだけでなく、より多くのループに適用できます。さらに、範囲ベースのforループには柔軟性があり、必要に応じて(returnまたはによってbreak)早く終了できるのに対し、for_eachスローする必要があります。
スティーブジェソップ

5
@SteveJessop:それでも、範囲ベースの可用性forにより、通常のit = c.begin(), const end = c.end(); it != end; ++itイディオムは機能しなくなります。
Ben Voigt

7
@SteveJessop for_each範囲ベースのforループに対するアルゴリズムの1つの利点は、またはができない ことです。つまり、体を見なくてもすぐにわかるので、そんなトリッキーはありません。breakreturnfor_each
bames53 2012

5
@Klaim:具体的には、たとえばstd::for_each(v.begin(), v.end(), [](int &i) { ++i; });と比較していfor (auto &i : v) { ++i; }ます。私は、柔軟性が両刃であることを受け入れます(goto非常に柔軟性があり、それが問題です)。私は考えていないように使用することができないという制約breakfor_each、それは需要の余分な冗長性のために、バージョンを補償-のユーザーfor_eachここでは、IMOという理論的概念の一種のため、実際の読みやすさと利便性を犠牲にしているfor_each、原則的に明確にし、概念的よりシンプル。実際には、それは明確でも単純でもありません。
スティーブジェソップ

10

swap頻度の低いカスタムバージョンを実装する必要があります。C ++ 03では、swapコストのかかるコピーのスローを回避するために、効率的な非スローがしばしば必要ですstd::swap。2つのコピーを使用するため、swap多くの場合、カスタマイズする必要があります。C ++では、std::swap使用moveしているため、効率的でスローされない移動コンストラクターと移動代入演算子の実装に焦点が移ります。これらの場合、デフォルトで十分な場合が多いため、これはC ++ 03の場合よりも作業がはるかに少なくなります。

一般的にどのイディオムを使用するかは、経験を通じて作成されているため、予測するのは困難です。「効果的なC ++ 11」はおそらく来年、「C ++ 11コーディング標準」は必要な経験がまだないため、3年でしか期待できません。


1
これは疑わしい。推奨されるスタイルは、移動とコピーの構築にスワップを使用することですが、循環するため、std :: swapは使用しません。
アランバルジュー

そうですが、通常、moveコンストラクターはカスタムスワップを呼び出すか、基本的に同等です。

2

名前はわかりませんが、C ++ 03コードでは、欠落した移動割り当ての代わりに次の構文を使用することがよくあります。

std::map<Big, Bigger> createBigMap(); // returns by value

void example ()
{
  std::map<Big, Bigger> map;

  // ... some code using map

  createBigMap().swap(map);  // cheap swap
}

これにより、swap上記と組み合わせたコピー省略によるコピーが回避されました。


1
あなたの例ではスワップは不要ですが、コピー省略はmapとにかく戻り値を構築します。表示する手法mapは、単に構築するだけでなく、すでに存在する場合に役立ちます。この例は、「安いデフォルトコンストラクター」コメントがなく、その構造とスワップの間に「// ...」を付けたほうがよいでしょう
Jonathan Wakely

私はあなたの提案に従ってそれを変更しました。ありがとう。
アンジェイ

「big」と「Bigger」の使用は混乱を招きます。キーのサイズと値のタイプがどのように重要であるかを説明しませんか?
einpoklum 2016年

1

C ++ 11標準を使用しているコンパイラーが次のコードでエラーにならないことに気付いたとき:

std::vector<std::vector<int>> a;

おそらくオペレーターが含まれているため>>、私は踊り始めました。以前のバージョンでは、実行する必要がありました

std::vector<std::vector<int> > a;

さらに悪いことに、これをデバッグしなければならなかった場合、これから出されるエラーメッセージがどれほど恐ろしいかを知っています。

ただし、これが「自明」かどうかはわかりません。


1
この機能は、以前のC ++ですでに追加されています。または、少なくともVisual C ++は、何年も前の標準の議論に従ってそれを実装しました。
アランBaljeu 2015

1
@AlanBaljeuもちろん、コンパイラ/ライブラリには標準以外のものが多く追加されています。C ++ 11の前に "auto"変数宣言を備えたコンパイラはたくさんありましたが、コードが実際に他のものでコンパイルできるかどうか確信が持てませんでした。問題は標準についてであり、「これを実行できるコンパイラーがあったか」ではありませんでした。
v010dya 2015

1

値による戻りは問題ではなくなりました。移動のセマンティクスや戻り値の最適化(コンパイラーに依存)を使用すると、オーバーヘッドやコスト(ほとんどの場合)がなく、コーディング機能がより自然になります。


...しかし、どのイディオムが廃止されましたか?
einpoklum 2016年

慣用句ではありませんが、もう必要ない良い習慣でした。コンパイラでサポートされているRVOでさえオプションです。 en.wikipedia.org/wiki/Return_value_optimization「C ++の進化の初期段階では、関数からクラス型のオブジェクトを効率的に返すことができないという弱点が弱点と見なされていました...」struct Data {char bytes [ 16]; }; void f(Data * p){// * pに直接結果を生成する} int main(){Data d; f(&d); }
Martin A

「値による戻りを回避するという慣習は、もはや他とは関係ありません。
アインポクルム2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.