8

私のライブラリは、次のようにレイアウトされた、いくつかのネストされた名前空間を使用します。

Library name
    Class name 1
    Class name 2
    Class name 3
    [...]
    Utilities
        Class name 1
            [...]
        Class name 2
            [...]
        Class name 3
            [...]
        [...]

「Utilities」名前空間には、実際のクラス自体に含めることを保証しない、各クラスへの便利な拡張機能が含まれています。

「ライブラリ名」名前空間は、他のライブラリとの広範な競合を回避するために必要です。「ユーティリティ」名前空間は、このようなものから生じるあいまいさのタイプを回避するために必要であり、その中の「クラス名」名前空間は、名前の衝突を回避します。同様のクラス用に作成されたユーティリティ。

それにもかかわらず、実際にはまだ非常に面倒です。たとえば、次のようにします。

MyLibrary::MyContainer<int> Numbers = MyLibrary::Utilities::MyContainer::Insert(OtherContainer, 123, 456);
// Oh God, my eyes...

これは、私が何か重大な間違いをしていると思わせてくれます。物事を整理し、直感的で明確に保つためのより簡単な方法はありますか?

4

4 に答える 4

15

標準ライブラリ (またはブースト) がどのように構成されているかを見てください。stdそのほぼすべてが単一の名前空間内にあります。すべてを独自の名前空間に配置しても、得られるものはほとんどありません。

Boost はほとんどのものを 内boostに置きますが、主要なライブラリは単一のサブ名前空間 ( boost::mpl、またはboost::filesystemなど) を取得します。auxまた、ライブラリは通常、内部実装の詳細のために単一のサブ名前空間を定義します。

しかし、通常、深いまたはきめの細かい名前空間階層は目にすることはありません。これは、それらを扱うのが面倒であり、メリットがほとんどまたはまったくないためです。

ここにいくつかの良い経験則があります:

特定のクラスに関連するヘルパー関数は、ADL が機能するように、クラスと同じ名前空間にある必要があります。次に、ヘルパー関数を呼び出すときにその名前を修飾する必要はまったくありません。(で定義されたイテレータsortの代わりに呼び出す方法と同様)。std::sortstd

それ以外については、名前空間の目的は名前の衝突を避けることであり、それ以外のことではないことを覚えておいてください。そのため、ユーザー コードとの衝突を避けるために、すべてのライブラリを名前空間に配置する必要がありますが、その名前空間内では、衝突する名前を導入する予定がない限り、さらなるサブ名前空間は技術的に必要ありません。

Boost のaux.

ただし、一般的には、ネストされた名前空間をできるだけ少なくすることをお勧めします。

そして最後に、私は自分の名前空間に短く、入力しやすく、読みやすい名前を使用することを強調する傾向があります (繰り返しますstdが、従うべき良い例です。短くて要点があり、ほとんどの場合、それ以上ネストされていませんそのため、頻繁に記述する必要がなく、ソース コードが乱雑になりすぎません。)

ヘルパー関数と ADL に関する最初のルールだけで、例を次のように書き直すことができます。

MyLibrary::MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

次に、次MyLibraryのように名前を変更できLibます。

Lib::MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

そして、あなたはかなり扱いやすいものになっています。

異なるクラスの同様のユーティリティ関数間で衝突があってはなりません。C++ では、関数をオーバーロードし、テンプレートを特殊化できるため、 と の両方をInsert(ContainerA)同じInsert(ContainerB)名前空間に持つことができます。

もちろん、名前空間とクラスの間の衝突は、実際に追加のネストされた名前空間がある場合にのみ可能です。

Library名前空間内で、どの名前を導入するかを決めるのはあなただけであることを思い出してください。したがって、衝突する名前を作成しないだけで、名前の衝突を回避できます。ユーザー コードとライブラリ コードを分離する名前空間は重要です。これは、この 2 つが互いを認識していない可能性があり、意図しない衝突が発生する可能性があるためです。

ただし、ライブラリ内では、衝突しない名前をすべて付けることができます。

于 2011-05-02T13:59:43.947 に答える
7

何かが痛い場合は、やめてください。C++ で深くネストされた名前空間を使用する必要はまったくありません。それらはアーキテクチャ デバイスとして意図されたものではありません。私自身のコードでは、常に単一レベルの名前空間を使用しています。

ネストされた名前空間の使用を主張する場合は、いつでもそれらの短いエイリアスを作成できます。

namespace Util = Library::Utility;

それから:

int x = Util::somefunc();   // calls Library::Utility::somefunc()
于 2011-05-02T13:26:19.690 に答える
3

ヘッダー ファイル内の宣言では、名前空間がグローバル名前空間を汚染しないようにする必要があります。

MyLibrary::Utilities::MyContainer<int> Numbers;

ただし、ソース ファイルでは usings を使用できます。

using namespace MyLibrary::Utilities;

...

MyContainer<int> Numbers;
Numbers.Insert(OtherContainer, 123, 456);
于 2011-05-02T13:23:29.767 に答える
2

完全修飾名は実際にはそれほど悪くはないように見えますが、メソッド名とクラス名を明示的にするのが好きです。しかし、using物事を助けることができます:

おそらくusing namespace MyLibrary、ソース ファイルで at global スコープを使用して、次のようにすることができます。

MyContainer<int> Numbers = Utilities::MyContainer::Insert(OtherContainer, 123, 456);

次に、必要な特定の関数をインポートできます。

using MyLibrary::Utilities::MyContainer::Insert

その後 MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

于 2011-05-02T13:26:15.930 に答える