2012-06-13 5 views
260

C++ 11ではinline namespaceが許可され、そのすべてのメンバーも自動的に同封されますnamespace。私はこれの有用な応用を考えることができません - inline namespaceが必要で、それが最も慣用的な解決策である状況の簡潔で簡潔な例を挙げてください。のインラインネームスペースとは何ですか?

(また、namespaceは異なるファイルに住むことができる1つでinlineではなく、すべての宣言を、宣言されたとき。これはトラブルのために物乞いないです何が起こるか私には明確ではない?)

答えて

259

インラインネームスペースは、symbol versioningに類似したライブラリバージョン管理機能ですが、特定のバイナリ実行形式(つまりプラットフォーム固有)の機能ではなく、C++ 11レベル(つまりクロスプラットフォーム) 。

これは、ライブラリ作成者がネストされた名前空間を表示し、その宣言が周囲の名前空間にあるように動作させるメカニズムです(インラインネームスペースをネストすることができるので、 "ネストネスト"最初の非インラインネームスペースに移動し、その宣言が間にあるネームスペースのいずれかにあるかのように見て行動します)。

例として、vectorのSTL実装について考えてみましょう。我々はC++ 98のヘッダ<vector>で、その後C++の初めから、インラインの名前空間を持っていた場合、このように見えたかもしれません:

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実装が選択されています。あなたのコードベースがpre-C++ 98回で書かれていて、vectorのC++ 98バージョンがあなたのコンパイラをアップグレードするときに問題を引き起こしていることが判明した場合、「すべて」というのは、 std::vectorをコードベースに置き換えて、std::pre_cxx_1997::vectorに置き換えてください。

は、次の標準に来て、STLのベンダーは、単に(C++ 11が必要です)emplace_backサポートでstd::vectorのための新しい名前空間を導入し、__cplusplus == 201103L IFFその1をインライン化、もう一度手順を繰り返します。

これで新しい言語機能が必要なのはなぜですか。私はすでに同じ効果を得るために次のことをすることができますか?

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の値に応じて、私は実装の1つまたは他のいずれかを取得します。

そして、あなたは、ほぼ正しいだろう。

// 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 

これはどこのユーザー完全に有効なコードです:

は、次の有効なC++ 98ユーザーコードを(それが完全にすでにC++ 98で名前空間stdに住んでテンプレートを特化することが許可された)考えてみましょうSTLの(自分のコピーにある)ものよりも効率的な実装を明らかに知っている、一連のタイプのベクトルの独自の実装を提供しています。

しかし:テンプレートを専門とするとき、あなたはそれがで宣言された名前空間にそうする必要がある標準は、ユーザーが合法タイプを特化することを予測する場合には、ですのでvectorは、名前空間stdで宣言されていることを述べています。

すなわちにおけるvector真の名前空間であることが確認された実装の詳細を公開するので、このコードは、非バージョンの名前空間std有する、またはC++ 11インライン・ネームスペース機能ではなく、using namespace <nested>を使用するバージョンのトリックと連携定義されたのはstdではありません。

は、ネストされた名前空間を(下のコメントを参照)を検出することができたことにより、他の穴がありますが、インライン名前空間には、それらをすべて接続します。そして、それがそこにあるのです。将来的には非常に便利ですが、AFAIK標準は独自の標準ライブラリのインラインネームスペース名を規定していません(私はこの点では間違っていることが分かりますが)。サードパーティのライブラリでのみ使用できます。コンパイラのベンダーが命名規則に同意しない限り、標準自体。

+13

Stroustrupの例で 'using namespace V99; 'が動作しない理由を説明するため+1。 –

+2

私は標準的な方法で標準ライブラリのバージョン管理は、サードパーティのバージョン管理と比較して制限されていると思います。もしC++ 21の 'vector'が私には役に立たず、C++ 11の' vector'が必要なら、それはC++ 21の急激な変化のせいかもしれません。しかし、それは私が最初に頼ってはいけない実装の詳細のためかもしれません。標準では、すべてのC++ 21実装が、同じベンダーの過去の 'std :: vector'とバグのある' std :: cxx_11 :: vector'を提供することを要求することはできません。サードパーティの図書館は、それが価値あると考えるならば、それを行うことができます。 –

+3

同様に、まったく新しいC++ 21実装を開始すると、 'std :: cxx_11'に古いナンセンスをたくさん実装することに負担をかけたくありません。すべてのコンパイラが標準ライブラリのすべての古いバージョンを実装するわけではありませんが、現時点では新しいものを追加するときに既存の実装を古いものにする必要はほとんどありません。とにかくです。私は、標準が有益に行うことができたものはオプションであるが、存在する場合は標準の名前であると考えている。 –

52

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 

名前空間Mineusing namespace V99;を入れていない理由を私はすぐに表示されていないが、私は完全に取るためにユースケースを理解する必要はありません委員会の動機付けに関するBjarneの言葉。

+0

だから、実際には最後の 'F(1)'バージョンはインライン 'V99'名前空間から呼び出されるのでしょうか? –

+1

@EitanT:はい、グローバル名前空間には 'using namespace Mine;'があり、 'Mine'名前空間には' Mine :: V99'というインライン名前空間のすべてが含まれています。 –

+0

しかしこれがうまくいくには、ファイルV99.hに 'inline'を追加する必要があります。ファイルV100.hが現れ、 'inline namespace V100'と宣言され、それを' Mine'にも含めるとどうなりますか? 'f(int)'は 'f(1)'によって呼び出されますか? – Walter

関連する問題