66

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

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

問題は、クラス内の名前空間の宣言を禁止する正当な理由 (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...
}

これは、名前空間の代わりにクラスを使用すると機能しますが、インスタンス化されないクラスを宣言することを余儀なくされます。これは完全な名前空間です。

入れ子になったクラスを許可し、クラス内で入れ子になった名前空間を禁止するのはなぜですか? それはレガシー負担ですか?


標準の一部(特に構文部分)を引用するだけではない意味論的な理由による回答は評価されます:)

4

6 に答える 6

58

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

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

クラス定義とその中で名前空間を宣言するという命題に関して、私はあなたを...

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

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

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

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

したがって、クラス内の名前空間は 7.3-p4 の定義に違反します。それが存在しないと仮定すると、クラス内を含むどこでも名前空間を宣言することが可能になりますが、クラスの定義は閉じられると形式化されるため、維持した場合、次のことを行う能力しか残されません。 7.3-p1 への準拠:

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

   .. more stuff ..

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

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

于 2012-11-21T00:35:16.157 に答える
28

私はここで他の人と意見を異にするつもりです。実質的なメリットがないとは言えません。余分な影響を与えずにコードを分離したい場合があります。例として、私はマルチスレッドリングバッファモジュールで作業していて、状態メンバーを分割したいと考えていました。そのうちのいくつかはアトミックおよび/またはメモリアラインされており、プロデューサーとコンシューマーの名前空間に分割したいと考えていました。

すべてにproducerorconsumerプレフィックスを付けて名前を付けるだけで (これは私の現在の厄介な実装です)、コードを読みにくくする汚染を追加しています。たとえば、プロデューサーが所有するすべてが で始まるproducer場合、それを読み取ったときに脳が誤っproducerProducerTimerて (プロデューサー タイマーのプロデューサー コピー) をproducerConsumerTimer(コンシューマー タイマーのプロデューサー シャドウ) またはconsumerProducerTimer(プロデューサー タイマーのコンシューマー シャドウ) として自動修正しやすくなります。コードがスキミングされなくなったため、デバッグに必要以上に時間がかかる。

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

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

理想的には、次のように変更できるようにしたいと考えています。

rbptr producerPosition;
rbptr consumerPosition;

これに:

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

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

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

于 2017-07-13T17:38:47.497 に答える
9

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

クラス内の名前空間によって何が得られるでしょうか? binary_tree::iterator::left_depth単にではなく、本当に言いたいですbinary_tree::left_depthか?内部に複数の名前空間がある場合は、それらを使用して saybinary_tree::depth_iterator::leftとを区別しますbinary_tree::breadth_iterator::right

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

于 2012-11-21T00:20:10.773 に答える
4

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

あなたが提案したのは単に名前空間の目的ではありません.2つの企業がコードまたは2つのアプリケーションを共有したい場合、これは真実ではありませんが、作成者はファイルのグローバルな知識を持っているため、1つのファイル内で異なる名前を付けることができます.彼らはいつでも衝突することを知りませんでした。

于 2012-11-21T00:15:24.727 に答える
3

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

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

// somewhere else

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

// use templated stuff.

個人的にはこの機能を楽しんでいますが、実装するほど需要がないようです。

于 2018-08-16T19:11:39.133 に答える