web-dev-qa-db-ja.com

C ++ 11との上位互換性の実現

複数のプラットフォームで実行する必要のある大規模なソフトウェアアプリケーションで作業しています。これらのプラットフォームの一部はC++ 11の一部の機能をサポートし(例:​​MSVS 2010)、一部はサポートしていません(例:GCC 4.3.x)。この状況は数年間続くと思います(私の推測では3〜5年)。

そこで、互換性インターフェイスをセットアップして、(可能な限り)最小限のメンテナンスで古いコンパイラでコンパイルできるC++ 11コードを人々が記述できるようにしたいと思います。全体として、目標は、#ifdefを可能な限り最小限に抑えながら、それらをサポートするプラットフォームで基本的なC++ 11構文/機能を有効にし、サポートしていないプラットフォームでエミュレーションを提供することです。

Std :: move()から始めましょう。互換性を実現する最も明白な方法は、次のようなものを共通のヘッダーファイルに入れることです。

#if !defined(HAS_STD_MOVE)
namespace std { // C++11 emulation
  template <typename T> inline T& move(T& v) { return v; }
  template <typename T> inline const T& move(const T& v) { return v; }
}
#endif // !defined(HAS_STD_MOVE)

これにより、人々は次のようなものを書くことができます

std::vector<Thing> x = std::move(y);

...免責。それは彼らがC++ 11で望んでいることをし、C++ 03でそれができる最善を尽くします。最後に最後のC++ 03コンパイラを削除しても、このコードはそのままにしておくことができます。

ただし、標準によれば、std名前空間に新しいシンボルを挿入することは違法です。それが理論です。私の質問は:実際的に言えば前方互換性を実現する方法としてこれを行うことに害はありますか?

12
mcmcc

私はC++プログラムで前方互換性と後方互換性のレベルを維持しながら、ずっと働いてきました 最終的にライブラリツールキットを作成する必要が生じるまで 、 リリースの準備をしています すでにリリースされています。一般的に、構文ではなく機能(フォワードエミュレートできないものもあります)でも "完全な"前方互換性が得られないことを受け入れる限り、マクロや代替の名前空間を使用する必要がありますいくつかのこと)そして、あなたはすべて準備が整いました。

C++ 03で実際に使用するのに十分なレベルでエミュレートできる機能はたくさんあります。たとえば、Boostなどの面倒な作業はありません。ヘック、nullptrのC++標準の提案でさえ、C++ 03バックポートを提案しています。そして、たとえばC++ 11のすべてにTR1がありますが、何年も前からプレビューがありました。それだけでなく、一部のC++ 14アサートバリアント、透過ファンクタ、optionalcanなどの機能をC +に実装する+03!

絶対にバックポートできないことがわかっている唯一の2つは、constexprテンプレートとvariadicテンプレートです。

名前空間stdに要素を追加することに関しては、問題ではない-であると私は考えています。最も重要で関連性の高いC++ライブラリの1つであるBoostと、それらのTR1の実装を考えてみてください:Boost.Tr1。 C++を改善したい場合は、C++ 11との上位互換性を確保し、定義によりnotC++ 03に変換するため、ブロックします。回避しようとする、またはとにかく残そうとする標準を超えた自分は、単純に言えば逆効果です。純粋主義者は文句を言うでしょうが、当然のことながら、それらを気にする必要はありません。

もちろん、結局のところ(03)の標準に従わないからといって、試してみることができない、またはgleefullyに行くことになるわけではありませんそれを壊す周り。それはポイントではありません。 std名前空間に何を追加するかについて非常に注意深い制御を維持し、ソフトウェアが使用される環境を制御できる(つまり、テストを行う)限り、追跡できないはずはありません。まったく害。可能であれば、すべてを別の名前空間で定義し、usingディレクティブを名前空間stdに追加するだけで、「絶対に」入れる必要があるもの以外に何も追加しないようにします。 Boost.TR1が行うことの多かれ少なかれ。


更新(2013):元の質問のリクエストとして、担当者がいないために追加できないコメントがいくつかあるので、C++ 11とC++のリストを示します。 14の機能とC++ 03への移植性の程度:

  • nullptr完全公式委員会のバックポートを考慮して実装可能。 「ネイティブ」タイプとして認識されるように、type_traitsの特殊化も提供する必要があります。
  • forward_list完全実装可能ですが、アロケータのサポートは、Tr1実装が提供できるものに依存しています。
  • 新しいアルゴリズム(partition_copyなど):完全実装可能。
  • ブレースシーケンスからのコンテナ構築(例:vector<int> v = {1, 2, 3, 4};):fully実装可能ですが、望みよりも幅が広くなります。
  • static_assert:マクロとして実装すると、ほぼ完全に実装可能です(コンマに注意するだけで十分です)。
  • unique_ptr:ほぼ完全に実装可能ですが、コードの呼び出しからのサポートも必要です(それらをコンテナーなどに格納するため)。以下を参照してください。
  • 右辺値参照:それらから得ることが期待される量に応じて、ほぼ完全に実装可能です(例:ブースト移動)。
  • Foreachの反復:ほぼ完全に実装可能で、構文は多少異なります。
  • 引数としてローカル関数を使用する(例:変換):ほぼ完全に実装可能ですが、構文は十分に異なります。たとえば、ローカル関数は呼び出しサイトで定義されておらず、直前に定義されています。
  • 明示的な変換演算子:実用的なレベルに実装可能(変換を明示的にする)。Imperfect C++の「explicit_cast」を参照してください。ただし、static_cast<>などの言語機能との統合はほぼ不可能です。
  • 引数の転送:右辺値の参照について上記で与えられた実用的なレベルに実装可能ですが、転送可能な引数を取る関数にNオーバーロードを提供する必要があります。
  • move:実用的なレベルに実装可能(上記の2つを参照)。もちろん、これから利益を得るために修飾子コンテナーとオブジェクトを使用する必要があります。
  • スコープアロケーター:Tr1の実装で支援できない限り、実際には実装できません。
  • マルチバイト文字タイプ:Tr1がサポートできない限り、実際には実装できません。ただし、意図した目的のために、C++ 11を使用している場合は、ICU、evenなどの問題を処理するために特別に設計されたライブラリに依存することをお勧めします。
  • 可変引数リスト:面倒で実装可能、引数の転送に注意を払います。
  • noexcept:コンパイラの機能によって異なります。
  • 新しいautoセマンティクスとdecltype:はコンパイラの機能に依存します-例:__typeof__
  • サイズ付き整数型(int16_tなど):コンパイラの機能によって異なります。または、ポータブルstdint.hに委任することもできます。
  • タイプ属性:コンパイラーの機能によって異なります。
  • 初期化リスト:私の知識では実装できません。ただし、シーケンスでコンテナを初期化する場合は、上記の「コンテナの構造」を参照してください。
  • テンプレートのエイリアシング:私の知識では実装できませんが、とにかくそれは不要な機能であり、テンプレートには::typeが永遠に含まれています
  • 可変テンプレート:私の知識では実装できません。 closeはテンプレート引数のデフォルトであり、N特殊化などが必要です。
  • constexpr:私の知る限りでは実装できません。
  • 均一な初期化:私の知識では実装できませんが、保証されたdefault-constructor初期化は、Boostの値で初期化して実装できます。
  • C++ 14 dynarray完全に実装可能。
  • C++ 14 optional<>:C++ 03コンパイラがアライメント設定をサポートしている限り、ほぼ完全に実装可能です。
  • C++ 14トランスペアレントファンクタ:ほぼ完全に実装可能ですが、クライアントコードでは、例:std::less<void>を明示的に使用して機能させる必要があります。
  • C++ 14の新しいアサートバリアント(assureなど):完全アサートが必要な場合は実装可能、代わりにスローを有効にする場合はほぼ完全に実装可能。
  • C++ 14タプル拡張(タイプごとにタプル要素を取得):fully実装可能で、機能の提案で説明されているとおりのケースでコンパイルできない場合もあります。

(免責事項:これらの機能のいくつかは、上でリンクしたC++バックポートライブラリに実装されているので、「完全に」または「ほぼ完全に」と言ったとき、私が話していることはわかっていると思います。)

9
Luis Machuca

これは根本的に不可能です。考慮してくださいstd::unique_ptr<Thing>。右辺値参照をライブラリとしてエミュレートすることが可能である場合、それは言語機能ではありません。

6
DeadMG
  1. Gccは4.3でC++ 11(当時はまだC++ 0x)の導入を開始しました。 この表 は、すでに右辺値参照とその他のあまり使用されていない機能があることを示しています(それらを有効にするには、-std=c++0xオプションを指定する必要があります)。
  2. C++ 11の標準ライブラリへの多くの追加はTR1ですでに定義されており、GNU stdlibc ++はstd :: tr1名前空間でそれらを提供します。したがって、適切な条件付き使用を行ってください。
  3. BoostはTR1関数のほとんどを定義し、TR1名前空間がない場合はそれらをTR1名前空間に挿入できます(ただし、GNU stdlibc ++を使用する場合はVS2010とgcc 4.3も同様です)。
  4. std名前空間に何かを入れることは「未定義の動作」です。つまり、仕様には何が起こるかは記載されていません。ただし、特定のプラットフォームで標準ライブラリが何かを定義していないことがわかっている場合は、先に進んで定義してください。必要なものと定義できるものを各プラットフォームで確認する必要があることを期待してください。
  5. 右辺値参照の提案、N169 は、C++ 03で移動セマンティクスを実装する方法について述べています。 unique_ptrの代わりに使用できます。ただし、移動のセマンティクスを実際に使用するコレクションに依存しており、C++ 03のコレクションは明らかにそうではないため、あまり役に立ちません。
2
Jan Hudec