C++11 では が許可されinline namespace
、そのすべてのメンバーは自動的にそれを囲む に含まれますnamespace
。これの便利な応用例が思いつきません。 が必要でinline namespace
、それが最も慣用的な解決策である状況の簡潔な例を誰か教えていただけませんか?
namespace
(また、 a が 1 つの宣言で宣言されていても、すべての宣言で宣言されていない場合、異なるファイルに存在する可能性があるので、何が起こるのかはわかりませんinline
。これはトラブルを招くのではないでしょうか。)
ベストアンサー1
インライン名前空間は、次のようなライブラリのバージョン管理機能です。シンボルのバージョン管理ただし、特定のバイナリ実行可能形式 (つまり、プラットフォーム固有) の機能ではなく、純粋に C++11 レベル (つまり、クロスプラットフォーム) で実装されています。
これは、ライブラリ作成者がネストされた名前空間を、そのすべての宣言が周囲の名前空間にあるかのように見せ、動作させることができるメカニズムです (インライン名前空間はネストできるため、「さらにネストされた」名前は最初の非インライン名前空間まで浸透し、その宣言が中間の名前空間のいずれかにあるかのように見せ、動作します)。
例として、 の STL 実装を考えてみましょうvector
。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 より前に記述されており、コンパイラをアップグレードしたときに の C++98 バージョンで問題が発生する場合は、コードベースでvector
への参照を見つけて に置き換えるだけで済みます。std::vector
std::pre_cxx_1997::vector
次の標準になると、STL ベンダーは手順を再度繰り返し、サポートstd::vector
付きの の新しい名前空間emplace_back
(C++11 が必要) を導入し、 iff をインライン化します__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 ユーザー コードを検討してください ( std
C++98 では、名前空間に存在するテンプレートを完全に特殊化することが既に許可されていました)。
// 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 (のコピー) にある実装よりも効率的な実装を明らかに知っている、型のセットに対して独自のベクトル実装を提供する、完全に有効なコードです。
ただし、テンプレートを特殊化する場合は、それが宣言された名前空間で行う必要があります。標準では、は名前空間でvector
宣言されているstd
ので、ユーザーは当然、そこで型を特殊化することを期待しています。
このコードは、バージョン管理されていない名前空間std
、または C++11 のインライン名前空間機能では機能しますが、 を使用したバージョン管理トリックでは機能しません。これは、 が直接定義されていないusing namespace <nested>
実際の名前空間の実装の詳細が公開されるためです。vector
std
ネストされた名前空間を検出できる穴は他にもありますが (以下のコメントを参照)、インライン名前空間はそれらすべてを塞ぎます。そして、それだけです。将来的には非常に便利ですが、私の知る限り、標準では独自の標準ライブラリにインライン名前空間名を規定していません (ただし、この点については間違っていることが証明されればうれしいです)。そのため、標準自体ではなく、サードパーティのライブラリにのみ使用できます (コンパイラ ベンダーが命名スキームに同意しない限り)。