C++ 11はinline namespace
sを許可します。そのすべてのメンバーは、それを囲むnamespace
にも自動的に含まれます。私はこれの有用な応用を考えることができません-誰かがinline namespace
が必要であり、それが最も慣用的な解決策である状況の簡潔で簡潔な例を与えることができますか?
(また、namespace
がすべての宣言ではなく、1つのinline
で宣言されている場合、何が起こるかは明確ではありません。これは異なるファイルに存在する可能性があります。
インライン名前空間は、 symbol versioning に似たライブラリバージョン管理機能ですが、特定のバイナリ実行可能形式(つまり、クロスプラットフォーム)の機能ではなく、純粋にC++ 11レベル(つまり、クロスプラットフォーム)で実装されます。プラットフォーム固有)。
これは、ライブラリの作成者がネストされた名前空間を、すべての宣言が周囲の名前空間にあるかのように見せて動作させるメカニズムです(インライン名前空間はネストできるため、「よりネストされた」名前は最初の非-名前空間をインライン化し、それらの宣言が間にある名前空間のいずれかにあるかのように見え、動作します。
例として、vector
のSTL実装を検討してください。 C++の最初からインライン名前空間がある場合、C++ 98では<vector>
ヘッダーは次のようになります。
namespace std {
#if __cplusplus < 1997L // pre-standard C++
inline
#endif
namespace pre_cxx_1997 {
template <class T> __vector_impl; // implementation class
template <class T> // e.g. w/o allocator argument
class vector : __vector_impl<T> { // private inheritance
// ...
};
}
#if __cplusplus >= 1997L // C++98/03 or later
// (ifdef'ed out b/c it probably uses new language
// features that a pre-C++98 compiler would choke on)
# if __cplusplus == 1997L // C++98/03
inline
# endif
namespace cxx_1997 {
// std::vector now has an allocator argument
template <class T, class Alloc=std::allocator<T> >
class vector : pre_cxx_1997::__vector_impl<T> { // the old impl is still good
// ...
};
// and vector<bool> is special:
template <class Alloc=std::allocator<bool> >
class vector<bool> {
// ...
};
};
#endif // C++98/03 or later
} // namespace std
__cplusplus
の値に応じて、いずれかのvector
実装が選択されます。コードベースがC++ 98以前のバージョンで記述されていて、コンパイラをアップグレードするときにvector
のC++ 98バージョンが問題を引き起こしていることがわかった場合、「すべて」を行う必要がありますコードベースでstd::vector
への参照を見つけて、std::pre_cxx_1997::vector
に置き換えます。
次の標準が来ると、STLベンダーはstd::vector
サポート(C++ 11を必要とする)でemplace_back
の新しい名前空間を導入し、__cplusplus == 201103L
をインライン化するだけで手順を繰り返します。 。
それでは、なぜこのために新しい言語機能が必要なのですか?同じ効果を得るために、すでに次のことを実行できますか?
namespace std {
namespace pre_cxx_1997 {
// ...
}
#if __cplusplus < 1997L // pre-standard C++
using namespace pre_cxx_1997;
#endif
#if __cplusplus >= 1997L // C++98/03 or later
// (ifdef'ed out b/c it probably uses new language
// features that a pre-C++98 compiler would choke on)
namespace cxx_1997 {
// ...
};
# if __cplusplus == 1997L // C++98/03
using namespace cxx_1997;
# endif
#endif // C++98/03 or later
} // namespace std
__cplusplus
の値に応じて、いずれかの実装を取得します。
そして、あなたはほとんど正しいでしょう。
次の有効なC++ 98ユーザーコードを考慮します(C++ 98のネームスペースstd
に既に存在するテンプレートを完全に特殊化することが許可されています)。
// I don't trust my STL vendor to do this optimisation, so force these
// specializations myself:
namespace std {
template <>
class vector<MyType> : my_special_vector<MyType> {
// ...
};
template <>
class vector<MyOtherType> : my_special_vector<MyOtherType> {
// ...
};
// ...etc...
} // namespace std
これは、ユーザーがSTL(のコピー)で見つかったものよりも効率的な実装を明らかに知っている型のセットのベクトルの独自の実装を提供する完全に有効なコードです。
But:テンプレートを特化するときは、宣言された名前空間で行う必要があります。標準では、vector
は名前空間std
であるため、ユーザーは当然、型を特殊化することを期待しています。
このコードは、バージョン管理されていない名前空間std
、またはC++ 11インライン名前空間機能で機能しますが、using namespace <nested>
を使用したバージョン管理のトリックでは機能しません。 vector
が定義されていたstd
は直接ではありませんでした。
ネストされた名前空間を検出できる他の穴がありますが(下記のコメントを参照)、インライン名前空間はそれらすべてをプラグインします。そして、それだけです。将来的には非常に便利ですが、AFAIK the Standardは独自の標準ライブラリのインライン名前空間名を規定していません(ただし、これについては間違っていることが証明されます)。したがって、サードパーティのライブラリでのみ使用できます。標準そのもの(コンパイラベンダーが命名スキームに同意しない限り)。
http://www.stroustrup.com/C++11FAQ.html#inline-namespace (Bjarne Stroustrupによって作成および管理されているドキュメント。ほとんどのC++ 11機能。)
それによると、後方互換性のためにバージョン管理を許可することです。複数の内部名前空間を定義し、最新のものをinline
にします。とにかく、バージョニングを気にしない人のためのデフォルトです。私は、最新のものは、まだデフォルトではない将来のバージョンまたは最先端のバージョンである可能性があると思います。
例は次のとおりです。
// file V99.h:
inline namespace V99 {
void f(int); // does something better than the V98 version
void f(double); // new feature
// ...
}
// file V98.h:
namespace V98 {
void f(int); // does something
// ...
}
// file Mine.h:
namespace Mine {
#include "V99.h"
#include "V98.h"
}
#include "Mine.h"
using namespace Mine;
// ...
V98::f(1); // old version
V99::f(1); // new version
f(1); // default version
名前空間Mine
にusing namespace V99;
を入れない理由はすぐにはわかりませんが、Bjarneの言葉を委員会の動機に取り入れるためにユースケースを完全に理解する必要はありません。
上記のすべての答えに加えて。
インライン名前空間を使用して、シンボル内のABI情報または関数のバージョンをエンコードできます。これは、ABIの下位互換性を提供するために使用されるためです。インライン名前空間を使用すると、リンカシンボル名のみに影響するため、APIを変更することなく、マングルされた名前(ABI)に情報を注入できます。
この例を考えてみましょう:
Foo
というオブジェクトへの参照を取り、何も返さないbar
関数を作成するとします。
Main.cppで言う
struct bar;
void Foo(bar& ref);
オブジェクトにコンパイルした後にこのファイルのシンボル名を確認する場合。
$ nm main.o
T__ Z1fooRK6bar
リンカのシンボル名は異なる場合がありますが、関数名と引数型はどこかに確実にエンコードされます。
現在、bar
は次のように定義されている可能性があります。
struct bar{
int x;
#ifndef NDEBUG
int y;
#endif
};
ビルドタイプに応じて、bar
は同じリンカシンボルを持つ2つの異なるタイプ/レイアウトを参照できます。
このような動作を防ぐために、構造体bar
をインラインネームスペースにラップします。ここでは、ビルドタイプに応じて、bar
のリンカーシンボルが異なります。
したがって、次のように書くことができます。
#ifndef NDEBUG
inline namespace rel {
#else
inline namespace dbg {
#endif
struct bar{
int x;
#ifndef NDEBUG
int y;
#endif
};
}
各オブジェクトのオブジェクトファイルを見ると、リリースを使用して1つをビルドし、デバッグフラグを使用してもう1つをビルドします。リンカシンボルにもインライン名前空間名が含まれていることがわかります。この場合
$ nm rel.o
T__ ZROKfoo9relEbar
$ nm dbg.o
T__ ZROKfoo9dbgEbar
リンカーシンボル名は異なる場合があります。
シンボル名にrel
およびdbg
が含まれていることに注意してください。
これで、デバッグをリリースモードまたはその逆にリンクしようとすると、ランタイムエラーとは反対にリンカーエラーが発生します。
実際に、インライン名前空間の別の使用法を発見しました。
Qt を使用すると、 Q_ENUM_NS
を使用して追加のニース機能が得られます。これには、Q_NAMESPACE
で宣言されたメタオブジェクトが含まれるネームスペースが必要です。ただし、Q_ENUM_NS
が機能するためには、同じファイルに対応するQ_NAMESPACE
が必要です-¹-。また、存在できるのは1つだけであるか、重複定義エラーが発生します。これは、事実上、すべての列挙が同じヘッダーにある必要があることを意味します。うん.
または...インライン名前空間を使用できます。 inline namespace
で列挙を非表示にすると、メタオブジェクトの名前が異なるマングルになりますが、追加のネームスペースが存在しないようにユーザーを探します。
したがって、何らかの理由でそれを行う必要がある場合、すべてをlookが1つの名前空間のように見える複数のサブ名前空間に分割するのに役立ちます。もちろん、これは、using namespace inner
を外側の名前空間に記述するのと似ていますが、 DRY 内側の名前空間の名前を2回記述する違反はありません。
実際にはそれよりも悪いです。同じブレースのセットにある必要があります。
完全に修飾せずにメタオブジェクトにアクセスしようとしない限り、メタオブジェクトは直接使用されることはほとんどありません。