web-dev-qa-db-ja.com

無名/匿名名前空間と静的関数

C++の機能は、次のように、名前なし(匿名)の名前空間を作成できることです。

namespace {
    int cannotAccessOutsideThisFile() { ... }
} // namespace

そのような機能は役に立たないと思うでしょう - 名前空間の名前を指定することはできないので、外部から名前空間の中の何かにアクセスすることは不可能です。しかし、これらの名前なしの名前空間areは、暗黙のusing節があるかのように、それらが作成されたファイル内でアクセス可能です。

私の質問は、なぜこれがいつ静的関数を使うよりも望ましいのかということです。それとも、まったく同じことをする2つの方法がありますか。

469
Head Geek

C++標準では、セクション7.3.1.1「名前空間なし」の段落2を参照してください。

名前空間スコープ内でオブジェクトを宣言するときはstaticキーワードを使用することは非推奨です。unnamed-namespaceは優れた代替手段を提供します。

静的はオブジェクト、関数、および無名共用体の名前にのみ適用され、型宣言には適用されません。

編集:

Staticキーワードのこの使用を廃止するという決定(翻訳単位内の変数宣言の可視性に影響を与える)は、逆になりました( ref )。この場合、静的または名前なしの名前空間を使用することは、まったく同じことを実行する2つの方法に戻ることです。詳しくは this SO questionをご覧ください。

名前なし名前空間には、翻訳単位ローカルタイプを定義できるという利点があります。詳しくは this SO questionをご覧ください。

これは私の注意を引くために信用は マイクパーシー に行きます。

309
luke

メソッドを匿名の名前空間に入れることで、誤って One Definition Rule に違反することを防ぐことができ、リンクする他のメソッドと同じようにヘルパーメソッドの名前を付けることを心配することがなくなります。

そして、lukeが指摘したように、匿名ネームスペースは静的メンバーよりも標準的に好まれています。

66
hazzen

静的が驚くべき効果をもたらすEdgeのケースが1つあります(少なくともそれは私には起こりました)。 C++ 03標準は14.6.4.2/1に述べています。

テンプレートパラメータに依存する関数呼び出しの場合、関数名が非修飾IDではなくではない場合template-idの場合、候補関数は次の点を除いて通常の検索規則(3.4.1、3.4.2)を使用して見つけられます。

  • 非修飾名ルックアップ(3.4.1)を使用したルックアップの部分では、テンプレート定義コンテキストからの外部リンケージを持つ関数宣言のみが見つかります。
  • 関連付けられた名前空間を使用した検索の一部(3.4.2)では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかで見つかった外部リンケージを持つ関数宣言のみが見つかりました。

...

以下のコードは、ご想像のとおりfoo(void*)ではなくfoo(S const &)を呼び出します。

template <typename T>
int b1 (T const & t)
{
  foo(t);
}

namespace NS
{
  namespace
  {
    struct S
    {
    public:
      operator void * () const;
    };

    void foo (void*);
    static void foo (S const &);   // Not considered 14.6.4.2(b1)
  }

}

void b2()
{
  NS::S s;
  b1 (s);
}

それ自体はそれほど大したことではないかもしれませんが、完全に準拠したC++コンパイラ(つまりexportをサポートしているもの)の場合、staticキーワードは他の方法では利用できない機能を持ちます。

// bar.h
export template <typename T>
int b1 (T const & t);

// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
  foo(t);
}

// foo.cc
#include "bar.h"
namespace NS
{
  namespace
  {
    struct S
    {
    };

    void foo (S const & s);  // Will be found by different TU 'bar.cc'
  }
}

void b2()
{
  NS::S s;
  b1 (s);
}

名前のない名前空間の関数がADLを使用してテンプレート内に見つからないようにする唯一の方法は、それをstaticにすることです。

Modern C++用に更新

C++ '11以降、名前のない名前空間のメンバは暗黙的に内部リンケージを持ちます(3.5/4)。

名前のない名前空間、または名前のない名前空間内で直接的または間接的に宣言された名前空間は内部リンケージを持ちます。

しかし同時に、14.6.4.2/1が更新されてリンケージの記述が削除されました(これはC++ '14から取られました)。

Postfix-expressionが従属名である関数呼び出しの場合、候補関数は以下を除いて通常の検索規則(3.4.1、3.4.2)を使用して見つけられます。

  • 非修飾名ルックアップ(3.4.1)を使用したルックアップの部分では、テンプレート定義コンテキストからの関数宣言のみが見つかります。

  • 関連付けられた名前空間(3.4.2)を使用した検索の部分では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかにある関数宣言のみが見つかります。

その結果、静的名前空間メンバーと名前なし名前空間メンバーの間のこの特別な違いはなくなりました。

33
Richard Corden

私は最近、コード内で静的キーワードを匿名ネームスペースに置き換える作業を始めましたが、すぐにネームスペース内の変数がデバッガで検査できなくなるという問題に遭遇しました。私はVC60を使っていたので、それが他のデバッガで問題ないかどうかわかりません。私の回避策は 'module'名前空間を定義することでした。そこに私のcppファイルの名前を付けました。

たとえば、私のXmlUtil.cppファイルでは、すべてのモジュール変数および関数に対して名前空間XmlUtil_I {...}を定義しています。こうすれば、変数にアクセスするためにデバッガでXmlUtil_I :: qualificationを適用できます。この場合、 '_ I'は、XmlUtilなどの他の場所で使用したいパブリック名前空間とは区別されます。

私がこのアプローチを真に匿名のものと比較して潜在的に不利な点は、他のモジュールで名前空間修飾子を使用することによって誰かが望ましい静的スコープに違反する可能性があることです。それが主な関心事であるかどうか私は知りません。

11
William Knight

そのためのstaticキーワードの使用は、C++ 98標準では推奨されていません。 staticの問題は型定義には適用されないということです。それはまた、さまざまなコンテキストでさまざまな方法で使用されるオーバーロードされたキーワードでもあるので、名前のない名前空間は物事を少し単純化します。

7
Firas Assaad

経験上、以前の静的関数を匿名名前空間に入れるC++の方法ではありますが、古いコンパイラではこれに問題が生じることがあります。私は現在、ターゲットプラットフォーム用にいくつかのコンパイラを使用していますが、より新しいLinuxコンパイラでは関数を匿名の名前空間に配置しても問題ありません。

しかし、Solaris上で実行されている古いコンパイラは、将来の不特定のリリースまでは許可されていますが、時にはそれを受け入れたり、エラーとして報告することもあります。エラーは私を心配させるものではありません、それはそれが可能性があるそれがしているときの許容するそれです。それで、私たちが全面的に近代化するまでは、匿名名前空間を好むところで静的(通常はクラススコープ)関数を使用しています。

6
Don Wakefield

さらに、この例のように変数にstaticキーワードを使用すると、次のようになります。

namespace {
   static int flag;
}

マッピングファイルには表示されません

3
Chris

あなたの質問を読んでいる間だけこの機能を学んだので、私は推測することができるだけです。これは、ファイルレベルの静的変数に比べていくつかの利点があります。

  • 匿名の名前空間は互いに入れ子にすることができ、シンボルがエスケープできないようにするための複数レベルの保護を提供します。
  • 同じソースファイルに複数の匿名ネームスペースを配置して、実質的に同じファイル内に異なる静的レベルのスコープを作成することができます。

誰かが実際のコードで匿名の名前空間を使ったことがあるかどうかを学ぶことに興味があります。

2

無名名前空間と静的関数の間のコンパイラ固有の違いは、次のコードをコンパイルすることでわかります。

#include <iostream>

namespace
{
    void unreferenced()
    {
        std::cout << "Unreferenced";
    }

    void referenced()
    {
        std::cout << "Referenced";
    }
}

static void static_unreferenced()
{
    std::cout << "Unreferenced";
}

static void static_referenced()
{
    std::cout << "Referenced";
}

int main()
{
    referenced();
    static_referenced();
    return 0;
}

VS 2017でこのコードをコンパイルする(有効にするためにレベル4警告フラグ/ W4を指定する 警告C4505:参照されていないローカル関数は削除されました )そして2017は未使用の静的関数に対してのみ警告を生成します。 gcc 4.9以上、および3.3以上のClangは、名前空間内の参照されていない関数に対する警告と、使用されていない静的関数に対する警告を生成します。

gcc 4.9とMSVC 2017のライブデモ

2
masrtis

個人的には、次のような理由から、名前のない名前空間よりも静的関数を好むのです。

  • それがコンパイルされた翻訳単位にプライベートであることは関数定義だけから明らかで明白です。名前のない名前空間では、関数が名前空間にあるかどうかを確認するためにスクロールして検索する必要があるかもしれません。

  • 名前空間内の関数は、一部の(古い)コンパイラでは外部関数として扱われることがあります。 VS2017では、彼らはまだexternです。このため、関数が名前なしの名前空間にある場合でも、それらを静的にマークすることをお勧めします。

  • 静的関数はCまたはC++でも非常によく似た動作をしますが、名前のない名前空間は明らかにC++のみです。名前のない名前空間は、字下げや私がそれを好きではない場合にも追加のレベルを追加します。

だから、私は関数のための静的の使用---うれしいです もう廃止予定ではありません

0
Pavel