web-dev-qa-db-ja.com

クラス内で名前空間を宣言できないのはなぜですか?

クラス内でクラスを宣言することは有効です。 (ネストされたクラス)

クラス内で名前空間を宣言することは無効です。

問題は、クラス内の名前空間の宣言を禁止する正当な理由(c ++文法/構文の問題以外)がありますか?


なぜ私がそれをしたいのか、ここに例があります:

二分木コンテナの基本的な宣言をしましょう

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  typedef left_depth_iterator_impl     left_depth_iterator;
  typedef right_depth_iterator_impl    right_depth_iterator;
  typedef left_breadth_iterator_impl   left_breadth_iterator;
  typedef right_breadth_iterator_impl  right_breadth_iterator;

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

クラスにイテレータがたくさんあることに気づいたので、次のように同じ名前空間内でイテレータを再グループ化します。

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  namespace iterator
  {
    typedef left_depth_iterator_impl     left_depth;
    typedef right_depth_iterator_impl    right_depth;
    typedef left_breadth_iterator_impl   left_breadth;
    typedef right_breadth_iterator_impl  right_breadth;
  }

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

これにより、簡単な使用が可能になります。

void  function()
{
  binary_tree::iterator::left_depth   it;

  ...stuff...
}

これは、名前空間の代わりにクラスを使用する場合に機能しますが、インスタンス化されないクラスを宣言することを強制されますが、これは非常に名前空間です。

ネストされたクラスを許可し、クラス内のネストされた名前空間を禁止する理由それはレガシーの負担ですか?


標準の一部(特に構文部分)を引用するだけでなく、セマンティックな理由による回答は評価されます:)

42
Drax

そのような機能を言語に追加しても、実際の利点はありません。通常、機能は需要がない限り追加されません。

クラス内の名前空間は何を買うでしょうか?単にbinary_tree::iterator::left_depthの代わりにbinary_tree::left_depthと言うのが本当ですか?おそらく、内部に複数の名前空間がある場合、それらを使用して、たとえばbinary_tree::depth_iterator::leftbinary_tree::breadth_iterator::rightを区別します。

いずれにせよ、内部クラスを貧しいプログラマーの名前空間として使用することで、目的の結果を達成できます。これは、クラス内に真の名前空間を要求しないさらに大きな理由です。

8
Adrian McCarthy

標準のマンデート名前空間の場所のどの部分を尋ねたので、最初にそれを見つけました:

C++ 11 7.3-p4:すべての名前空間定義は、グローバルスコープまたは名前空間スコープ(3.3.6)に表示されます。

クラス定義と名前空間内の宣言の提案に関して、私はあなたを...

C++ 11 9.2-p2:クラスは、クラス指定子の終了}で完全に定義されたオブジェクトタイプ(3.9)(または完全なタイプ)と見なされます。クラスメンバ仕様内では、クラスは関数本体、デフォルト引数、例外仕様、および非静的データメンバのブレースまたはイコール初期化子内で完全と見なされます(ネストされたクラスのようなものを含む)。それ以外の場合は、独自のクラスメンバー仕様内で不完全と見なされます。

エルゴ、閉じているカーリーに到達すると、クラス定義は有限になります。開いて拡張することはできません(派生は別のものですが、定義したクラスを拡張しません)。

しかし、名前空間の標準定義の最初に潜んでいるのは、名前空間を拡張する機能です。より良い用語がないためにそれを拡張するには:

C++ 7.3-p1:名前空間は、オプションで名前が付けられた宣言領域です。名前空間の名前を使用して、その名前空間で宣言されたエンティティにアクセスできます。つまり、名前空間のメンバー。 他の宣言領域とは異なり、名前空間の定義は1つ以上の翻訳単位の複数の部分に分割できます。(強調が追加されました)。

したがって、クラス内の名前空間は7.3-p4の定義に違反します。それが存在しないと仮定すると、possibleはクラス内を含むどこでも名前空間を宣言しますが、クラスの定義は一度それが形式化されるためが閉じている場合、7.3-p1への準拠を維持した場合、次の操作のみを行うことができます。

class Foo
{
   namespace bar
   {
       ..stuff..
   }

   .. more stuff ..

   namespace bar
   {
       ..still more stuff..
   }
};

このfeatureの有用性は、それを解決するために7.3-p4が確立される前に、約3フル秒間議論される可能性がありました。

45
WhozCraig

私はここで他の人に反対するつもりです。本当の利点はないとは言いません。時々、余分な影響なしにコードを分離したいだけです。例として、私はマルチスレッドリングバッファモジュールで作業しており、状態メンバー(一部はアトミックおよび/またはメモリアラインメント)をプロデューサーとコンシューマーのネームスペースに分割したいと考えていました。

すべてにproducerまたはconsumerプレフィックス(これは現在の迷惑な実装です)を付けるだけで、コードを読みにくくする汚染を追加しています。例えば。プロデューサーが所有するすべてのものがproducerで始まる場合、誤ってproducerProducerTimer(プロデューサータイマーのプロデューサーコピー)をproducerConsumerTimer(プロデューサーシャドウコンシューマータイマーの)またはconsumerProducerTimer(プロデューサータイマーのコンシューマーシャドウ)。コードがスキム可能でないため、必要以上に時間がかかるデバッグ。

ネストされたクラス/構造を作成することにより:

  • このコードを管理する次の開発者に、これらの複数をインスタンス化、コピー、およびコンテキスト内で相互に割り当てることができるという考えを与えることができるので、命名を心配する代わりに、= delete これらの事。
  • 他の方法では必要ないかもしれない構造的な配置パディングを使用して、コンテキストにメモリフットプリントを追加することができます。
  • すべてのメンバーを静的にすることは、独自のプロデューサー/コンシューマー状態変数を必要とする複数のコンテキストをインスタンス化できるため、オプションではありません。
  • このような構造体の関数は、両側で共有される定数や関数など、他のメンバーデータや関数にアクセスできなくなりましたが、代わりにこれらを引数としてとる必要があります。

理想的には、次のようなものを変更できるようにしたいと思います。

rbptr producerPosition;
rbptr consumerPosition;

これに:

namespace producer
{
    rbptr position;
}
namespace consumer
{
    rbptr position;
}

次に、コンシューマメンバにのみ接触する関数はコンシューマネームスペースを使用でき、プロデューサメンバにのみ接触する関数はプロデューサネームスペースを使用でき、両方に触れる必要がある関数は両方とも明示的に修飾する必要があります。プロデューサー名前空間のみを使用している関数のコンシューマー変数に誤って触れる方法はありません。

この場合、純粋に物のプロデューサーとコンシューマーのコピーの間の名前の衝突を減らし、名前の衝突を減らすことがネームスペースの目的です。そのため、クラス内で名前空間を宣言できるという提案を支持します。

13
ErsatzStoat

これは名前空間のポイントではありません。名前空間は、2つの異なる企業(またはコードベース)が互いにコードを混在させることができるように、コードのトップレベルの近くに存在することを意図しています。さらにマイクロレベルでは、電子メールアクセス用のIMAPと電子メール送信用のSMTPの両方を使用してコーディングし、Emailと呼ばれるいずれかのモジュールにまったく異なるクラスがありますが、同じクラスの両方を使用したいアプリケーション、例えばメールクライアント、おそらく、あるアカウントから別のアカウントにメールを転送します。名前空間/パッケージ名/などはこれを許可します。

あなたが提案したのは、名前空間の目的ではありません-1つのファイル内で、作成者はファイルのグローバルな知識を持っているため、別の名前を付けることができますが、2つの会社がコードまたは2つのアプリケーションを共有したい場合はそうではありません彼らはどの時点でも衝突することを知らなかった。

4
djechlin

私が言及した価値があると感じたほんの小さな考え。クラス内での名前空間の使用の1つは、テンプレート化された名前空間と機能的に同等です。

template<class...types>
struct Namespace {
    namespace Implementation {
        ...
    }
};

// somewhere else

using namespace Namespace<types...>::Implementation;

// use templated stuff.

個人的には、この機能をお楽しみいただけますが、実装するには需要が十分に高くないようです。

1