サーチ…


前書き

複数のライブラリを使用するときの名前の衝突を防ぐために使用されます。名前空間は、関数、クラス、型などの宣言的な接頭辞です。

構文

  • 名前空間識別子opt ){ 宣言-seq }
  • インラインネームスペース識別子opt ){ 宣言-seq } / * C ++ 11以降* /
  • インライン( opt )名前空間属性指定子 - seq 識別子opt ){ 宣言-seq } / * C ++以降17 * /
  • 名前空間enclosing-namespace-specifier :: identifier { declaration-seq } / * C ++以降17 * /
  • 名前空間識別子 = 修飾された名前空間指定子 ;
  • namespace ネストされた名前指定子optnamespace-nameを使用します。
  • name -names-names -specifieroptnamespace-nameを使用した属性指定子 - seq 。 / * C ++ 11以降* /

備考

キーワード namespaceは、文脈によって3つの異なる意味があります。

  1. オプション名と中括弧で囲まれた宣言シーケンスの後には、新しい名前空間を定義するか、それらの宣言で既存の名前空間拡張します 。名前を省略すると、名前空間は名前のない名前空間になります

  2. その後に名前と等号が続くと、 名前空間のエイリアスが宣言されます

  3. 名前空間名usingして後に続けると、 usingディレクティブが形成されます。これにより、指定された名前空間内の名前が修飾されていない名前検索によって検出されます(しかし、現在のスコープ内の名前は再宣言されません)。クラススコープでusingディレクティブを使用することはできません。

using namespace std;落胆している。どうして? namespace stdは巨大なので!これは、名前が衝突する可能性が高いことを意味します。

//Really bad!
using namespace std;

//Calculates p^e and outputs it to std::cout
void pow(double p, double e) { /*...*/ }

//Calls pow
pow(5.0, 2.0); //Error! There is already a pow function in namespace std with the same signature,
               //so the call is ambiguous

名前空間とは何ですか?

C ++名前空間は、C ++エンティティ(関数、クラス、変数)の集合であり、名前の前に名前空間の名前が付いています。名前空間内にコードを書くとき、その名前空間に属する名前付き実体には名前空間名の接頭語を付ける必要はないが、その外の実体は完全修飾名を使用しなければならない。完全修飾名の形式は<namespace>::<entity>です。例:

namespace Example
{
  const int test = 5;

  const int test2 = test + 12; //Works within `Example` namespace
}

const int test3 = test + 3; //Fails; `test` not found outside of namespace.

const int test3 = Example::test + 3; //Works; fully qualified name used.

名前空間は、関連する定義をまとめてグループ化するのに便利です。ショッピングモールの類推を取る。一般的に、ショッピングモールはいくつかの店舗に分かれており、各店舗は特定のカテゴリーの商品を販売しています。ある店がエレクトロニクスを売っているのに対し、別の店が靴を売っているかもしれない店舗タイプのこれらの論理的な分離は、買い物客が探している商品を見つけるのに役立ちます。ネームスペースは、買い物客のようなc ++プログラマが論理的に整理することで、探している関数、クラス、変数を見つけるのに役立ちます。例:

namespace Electronics
{
    int TotalStock;
    class Headphones
    {
        // Description of a Headphone (color, brand, model number, etc.)
    };
    class Television
    {
        // Description of a Television (color, brand, model number, etc.)
    };
}

namespace Shoes
{
    int TotalStock;
    class Sandal
    {
        // Description of a Sandal (color, brand, model number, etc.)
    };
    class Slipper
    {
        // Description of a Slipper (color, brand, model number, etc.)
    };
}

一つの名前空間があらかじめ定義されています。名前空間はありませんが、 ::で表すことができるグローバル名前空間です。例:

void bar() {
    // defined in global namespace
}
namespace foo {
    void bar() {
        // defined in namespace foo
    }
    void barbar() {
        bar();   // calls foo::bar()
        ::bar(); // calls bar() defined in global namespace
    }
}

名前空間を作る

名前空間を作成するのは簡単です:

//Creates namespace foo
namespace Foo
{
    //Declares function bar in namespace foo
    void bar() {}
}

barを呼び出すには、名前空間を最初に指定し、次にスコープ解決演算子::続けて指定する必要があります。

Foo::bar();

ある名前空間を別の名前空間に作成することができます。たとえば、次のようになります。

namespace A
{
    namespace B
    {
        namespace C
        {
            void bar() {}
        }
    }
}
C ++ 17

上記のコードは以下のように単純化することができます:

namespace A::B::C
{
    void bar() {}
}

名前空間の拡張

namespaceの便利な機能は、それらを拡張すること(メンバーを追加すること)です。

namespace Foo
{
    void bar() {}
}

//some other stuff

namespace Foo
{
    void bar2() {}
}

ディレクティブの使用

キーワード'using'には3つの味があります。キーワード 'namespace'と組み合わせると、 'using directive'と書くことができます:

名前空間Fooすべての要素の前にFoo::を書かない場合は、名前空間Fooを使用してusing namespace Foo;できますusing namespace Foo;すべてのものをFooからインポートします。

namespace Foo
{
    void bar() {}
    void baz() {}
}

//Have to use Foo::bar()
Foo::bar();

//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK

名前空間全体ではなく、名前空間で選択したエンティティをインポートすることもできます。

using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported

ほとんどの場合、ヘッダーファイルにusing namespaceすることは悪いスタイルとして見られます。これが完了すると、ヘッダーを含むすべてのファイルに名前空間がインポートさます。名前空間usingusingない」方法はないので、これは名前空間の汚染(グローバル名前空間のより多くの、あるいは予期しない記号)、あるいはそれ以上の衝突を招く可能性があります。問題の図解については、次の例を参照してください。

/***** foo.h *****/
namespace Foo
{
    class C;
}

/***** bar.h *****/
namespace Bar
{
    class C;
}

/***** baz.h *****/
#include "foo.h"
using namespace Foo;

/***** main.cpp *****/
#include "bar.h"
#include "baz.h"

using namespace Bar;
C c; // error: Ambiguity between Bar::C and Foo::C

クラススコープでusingディレクティブを使用することはできません。

引数依存ルックアップ

明示的な名前空間修飾子なしで関数を呼び出すとき、コンパイラは、その関数のパラメータ型の1つがその名前空間にも含まれている場合、名前空間内の関数を呼び出すことを選択できます。これは「引数依存ルックアップ」またはADLと呼ばれます。

namespace Test
{
  int call(int i);

  class SomeClass {...};

  int call_too(const SomeClass &data);
}

call(5); //Fails. Not a qualified function name.

Test::SomeClass data;

call_too(data); //Succeeds

Test名前空間からのパラメータ型が1つもないため、 callが失敗します。 SomeClassTestメンバーであるため、 call_too機能するため、ADLルールの対象となります。

いつADLが起こらないか

通常の非修飾ルックアップでクラスメンバー、ブロックスコープで宣言された関数、または関数型ではないものが見つかった場合、ADLは発生しません。例えば:

void foo();
namespace N {
    struct X {};
    void foo(X ) { std::cout << '1'; }
    void qux(X ) { std::cout << '2'; }
}

struct C {
    void foo() {}
    void bar() {
        foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments
    }
};

void bar() {
    extern void foo(); // redeclares ::foo
    foo(N::X{});       // error: ADL is disabled and ::foo() doesn't take any arguments
}

int qux;

void baz() {
    qux(N::X{}); // error: variable declaration disables ADL for "qux"
}

インライン名前空間

C ++ 11

inline namespaceは、 inline namespaceの内容を囲むネームinline namespace含めるので

namespace Outer
{
    inline namespace Inner
    {
        void foo();
    }
}

ほとんど同等です

namespace Outer
{

    namespace Inner
    {
        void foo();
    }

    using Inner::foo;
}

Outer::Inner::およびOuter::関連付けられた要素は同一です。

以下は同等です

Outer::foo();
Outer::Inner::foo();

using namespace Inner;別のusing namespace Inner;テンプレートの特殊化として扱いにくい部分については同等ではありません。

にとって

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
    template <>
    void foo<MyCustomType>() { std::cout << "Specialization"; }
}
  • インラインネームスペースは、 Outer::foo特殊化を可能にします。

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        inline namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
    }
    
  • using namespace使用すると、 Outer::foo特殊化は許可されません

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
        using namespace Inner;
        // Specialization of `Outer::foo` is not possible
        // it should be `Outer::Inner::foo`.
    }
    

インラインネームスペースは、いくつかのバージョンを共存させてinline 1にデフォルト化させる方法です

namespace MyNamespace
{
    // Inline the last version
    inline namespace Version2
    {
        void foo(); // New version
        void bar();
    }

    namespace Version1 // The old one
    {
        void foo();
    }

}

使い方

MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo();           // default version : MyNamespace::Version1::foo();

無名/匿名の名前空間

名前のない名前空間を使用すると、名前に内部リンケージがあることを確認することができます(現在の翻訳単位でしか参照できません)。そのような名前空間は、他の名前空間と同じ方法で定義されますが、名前はありません。

namespace {
    int foo = 42;
}

fooは、表示される翻訳単位でのみ表示されます。

ヘッダーファイルに名前のない名前空間を使用しないことをお勧めします。これは、それが含まれるすべての翻訳単位のコンテンツのバージョンを与えるためです。これは、非constグローバルを定義する場合に特に重要です。

// foo.h
namespace {
    std::string globalString;
}

// 1.cpp
#include "foo.h" //< Generates unnamed_namespace{1.cpp}::globalString ...

globalString = "Initialize";

// 2.cpp
#include "foo.h" //< Generates unnamed_namespace{2.cpp}::globalString ...

std::cout << globalString; //< Will always print the empty string

コンパクトなネストされた名前空間

C ++ 17
namespace a {
  namespace b {
    template<class T>
    struct qualifies : std::false_type {};
  }
}

namespace other {
  struct bob {};
}

namespace a::b {
  template<>
  struct qualifies<::other::bob> : std::true_type {};
}

あなたは、両方の入力ができabとのワンステップで名前空間をnamespace a::b C ++ 17で開始します。

長い名前空間のエイリアス

これは、通常、ライブラリのコンポーネントを参照するような長い名前空間の参照の名前を変更する、または短縮するために使用されます。

namespace boost
{
    namespace multiprecision
    {
        class Number ...
    }
}

namespace Name1 = boost::multiprecision;


//    Both Type declarations are equivalent
boost::multiprecision::Number X   //    Writing the full namespace path, longer
Name1::Number Y                   //    using the name alias, shorter

エイリアス宣言のスコープ

エイリアス宣言は前のusingステートメントの影響を受けます

namespace boost
{
    namespace multiprecision
    {
        class Number ...
    }
}


using namespace boost;

//   Both Namespace are equivalent 
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;

しかし、あなたが次のようなものを持っているときに、あなたがエイリアシングしている名前空間を混乱させる方が簡単です。

namespace boost
{
    namespace multiprecision
    {
        class Number ...
    }
}

namespace numeric
{
    namespace multiprecision
    {
        class Number ...
    }
}

using namespace numeric;
using namespace boost;

//    Not recommended as 
//    its not explicitly clear whether Name1 refers to
//    numeric::multiprecision or boost::multiprecision
namespace Name1 = multiprecision;

//    For clarity, its recommended to use absolute paths
//    instead
namespace Name2 = numeric::multiprecision;
namespace Name3 = boost::multiprecision;

名前空間エイリアス

名前空間は、 namespace 識別子 =構文を使用してエイリアス( つまり、同じ名前空間の別の名前)を与えることができます。エイリアス名空間のメンバには、エイリアスの名前で修飾することでアクセスできます。次の例では、ネストされた名前空間AReallyLongName::AnotherReallyLongNameは入力に不便なので、関数quxはエイリアスNローカルで宣言しています。その名前空間のメンバーは、 N::を使用して簡単にアクセスできます。

namespace AReallyLongName {
    namespace AnotherReallyLongName {
        int foo();
        int bar();
        void baz(int x, int y);
    }
}
void qux() {
    namespace N = AReallyLongName::AnotherReallyLongName;
    N::baz(N::foo(), N::bar());
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow