Skip to content

Latest commit

 

History

History
246 lines (197 loc) · 7.87 KB

inline_namespaces.md

File metadata and controls

246 lines (197 loc) · 7.87 KB

インライン名前空間

  • cpp11[meta cpp]

概要

インライン名前空間 (inline namespace)は、名前空間内の機能に透過的にアクセスするための機能である。inline namespaceによって定義した名前空間の機能には、その名前空間を指定しなくてもアクセスできる。

namespace my_namespace {
  inline namespace features {
    void f() {}
  }
}

int main()
{
  my_namespace::features::f();
  my_namespace::f();           // features名前空間は省略できる
}

この機能は、以下の用途に使用できる:

  • using namespaceによる名前空間省略の階層を段階的に指定する
  • APIのバージョニング

仕様

  • 名前空間のinline指定は、名前付き名前空間と無名名前空間の定義で使用できる。inline指定された名前空間を「インライン名前空間 (inline namespace)」と呼ぶ

    inline namespace my_namespace {}
    inline namespace {}
  • インライン名前空間のメンバは、その外側の名前空間 (the enclosing namespace, それを取り囲む名前空間) のメンバとして使用できる

  • インライン名前空間とその外側の名前空間は、引数依存の名前探索で探索される「関連ある名前空間(associated namespace)」となる

    #include <iostream>
    
    namespace ns1 {
      class X {};
    
      inline namespace ns2 {
        class Y {};
    
        void f(X)
        {
          std::cout << "call f()" << std::endl;
        }
      }
    
      void g(Y)
      {
        std::cout << "call g()" << std::endl;
      }
    }
    
    int main()
    {
      f(ns1::X());      // 「call f()」が出力される
      g(ns1::Y());      // 「call g()」が出力される
      g(ns1::ns2::Y()); // 「call g()」が出力される
    }
  • インライン名前空間の外側の名前空間をusingディレクティブに指定することで、インライン名前空間のメンバがその外側の名前空間のメンバとして暗黙的に挿入される

    #include <iostream>
    
    namespace ns1 {
      inline namespace ns2 {
        void f()
        {
          std::cout << "call f()" << std::endl;
        }
      }
    }
    
    int main()
    {
      using namespace ns1;
      f();
    }
  • インライン名前空間のメンバは、外側の名前空間で外側の名前空間のメンバであるかのように、明示的にインスタンス化、および明示的に特殊化できる

    #include <iostream>
    
    namespace ns1 {
      inline namespace ns2 {
        template <class T>
        struct X {
          static constexpr int value = 0;
        };
      }
    
      // インライン名前空間で定義されたクラステンプレートを
      // 明示的にインスタンス化
      template struct X<int>;
    
      // インライン名前空間で定義されたクラステンプレートを
      // 明示的に特殊化
      template <>
      struct X<void> {
        static constexpr int value = 1;
      };
    }
    
    int main()
    {
      std::cout << ns1::X<int>::value << std::endl;       // 0が出力される
      std::cout << ns1::X<void>::value << std::endl;      // 1が出力される
      std::cout << ns1::ns2::X<void>::value << std::endl; // 1が出力される
    }
  • インライン名前空間の外側の名前空間の機能に、明示的な名前空間修飾付きでアクセスした場合でも、インライン名前空間をusingディレクティブしたのと同様にそのインライン名前空間の機能が外側の名前空間に持ち込まれる。これは、外側の名前空間とインライン名前空間で同名のメンバが定義されたときに、名前衝突による曖昧さが発生することを意味する

    namespace ns1 {
      inline namespace ns2 {
        int a;
      }
      int a;
    }
    
    int main()
    {
      ns1::a = 0; // ns2で同名の変数が定義されているため、曖昧になる
    }
  • 翻訳単位は、std名前空間をインライン名前空間として宣言してはならない

using namespaceによる名前空間省略の階層を段階的に指定する

インライン名前空間を使用することで、using namespaceの影響範囲をユーザーが選択できるようになる。

namespace my_namespace {
  inline namespace features {
    void f() {}
  }

  void g() {}
}

int main()
{
  {
    // my_namespace::features名前空間も含む
    // my_namespace名前空間以下の機能を、名前空間修飾なしで
    // アクセスできるようにする
    using namespace my_namespace;
    f();
    g();
  }
  {
    // my_namespace::features名前空間以下の機能だけを
    // 名前空間修飾なしでアクセスできるようにする
    using namespace my_namespace::features;
    f();
    my_namespace::g();
  }
}

出力

APIのバージョニング

インライン名前空間をデフォルトのバージョンとし、古いAPIを元の名前空間でそのまま残すようにできる。

デフォルトのバージョンを切り替える際は、デフォルトバージョンにする名前空間をインライン名前空間に変更し、デフォルトバージョン以外の名前空間を非インライン名前空間に変更する。

これによって、バイナリ互換性を保つバージョニングをしやすくする。

#include <iostream>

namespace my_namespace {
  namespace v1 {
    void f()
    {
      std::cout << "v1" << std::endl;
    }
  }

  inline namespace v2 {
    void f()
    {
      std::cout << "v2" << std::endl;
    }
  }
}

int main()
{
  my_namespace::v1::f(); // 古いバージョンのAPIを呼び出す
  my_namespace::v2::f(); // バージョンを明示的に指定してAPIを呼び出す
  my_namespace::f();     // デフォルトバージョンのAPIを呼び出す
}

出力

v1
v2
v2

関連項目

参照