116

私は C++ から Java と C# に切り替えましたが、そこでは名前空間/パッケージの使用がはるかに優れていると思います (適切に構造化されています)。その後、C++ に戻り、同じ方法で名前空間を使用しようとしましたが、必要な構文がヘッダー ファイル内でひどいものでした。

namespace MyCompany
{
    namespace MyModule
    {
        namespace MyModulePart //e.g. Input
        {
            namespace MySubModulePart
            {
                namespace ...
                {
                    public class MyClass    

以下も私には奇妙に思えます(深いインデントを避けるため):

namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
     public class MyClass
     {

上記のことを表現する短い方法はありますか?私は何かが欠けています

namespace MyCompany::MyModule::MyModulePart::...
{
   public class MyClass

アップデート

わかりました、Java/C# と C++ での使用の概念が異なると言う人もいます。本当に?名前空間の目的は (動的な) クラスのロードだけではないと思います (これは非常に技術的な理由による観点です)。読みやすさと構造化のために使用しないでください。たとえば、「IntelliSense」を考えてみてください。

現在、名前空間とそこにあるものとの間にロジック/接着剤はありません。Java と C# はこれをはるかに優れた方法で行います...<iostream>名前空間を含めて持つのはなぜstdですか? わかりました。ロジックがインクルードするヘッダーに依存する必要があると言うなら、なぜ #include は or のような「IntelliSense」に適した構文を使用しないのです#include <std::io::stream><std/io/stream>? デフォルトのライブラリに構造化が欠けていることは、Java/C# と比較した C++ の弱点の 1 つだと思います。

熾烈な衝突への独自性を1つのポイント(C#やJavaのポイントでもあります)なら、プロジェクト名や会社名を名前空間として使うのもいいアイデアだと思いませんか?

一方で、C++ が最も柔軟であると言われています... しかし、誰もが「これを行うな」と言いましたか? C++ は多くのことを実行できるように思えますが、多くの場合、C# と比較して最も簡単なことに対してさえ構文がひどいものです。

更新 2

ほとんどのユーザーは、2 つのレベルよりも深いネストを作成するのはナンセンスだと言います。では、Win8 開発における Windows::UI::Xaml および Windows::UI::Xaml::Controls::Primitives 名前空間はどうでしょうか。Microsoft の名前空間の使用法は理にかなっていると思います。実際、それは単なる 2 レベルよりも深いものです。より大きなライブラリ/プロジェクトには、より深いネストが必要だと思います (ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace のようなクラス名は嫌いです... そうすれば、すべてをグローバル名前空間にも入れることができます)。

更新 3 - まとめ

ほとんどの人は「やらない」と言いますが...ブーストでさえ、1つまたは2つのレベルよりも深いネストがあります。はい、それはライブラリですが、再利用可能なコードが必要な場合は、独自のコードを他の誰かに提供するライブラリのように扱ってください。また、名前空間を使用して発見目的でより深いネストを使用します。

4

11 に答える 11

161

C++17 は、ネストされた名前空間の定義を簡素化する可能性があります。

namespace A::B::C {
}

と同等です

namespace A { namespace B { namespace C {
} } }

cppreference の名前空間ページの (8) を参照してください:
http://en.cppreference.com/w/cpp/language/namespace

于 2015-01-19T10:25:50.613 に答える
31

非常に深いインデントを避けるために、通常は次のようにします。

namespace A { namespace B { namespace C
{
    class X
    {
        // ...
    };
}}}
于 2014-12-12T15:40:46.080 に答える
17

私はピーターチェンの答えを完全に支持しますが、あなたの質問の別の部分に対処する何かを追加したいと思います.

名前空間の宣言は、私が実際に s の使用を好む C++ の非常にまれなケースの 1 つです#define

#define MY_COMPANY_BEGIN  namespace MyCompany { // begin of the MyCompany namespace
#define MY_COMPANY_END    }                     // end of the MyCompany namespace
#define MY_LIBRARY_BEGIN  namespace MyLibrary { // begin of the MyLibrary namespace
#define MY_LIBRARY_END    }                     // end of the MyLibrary namespace

これにより、名前空間の閉じ括弧の近くにコメントする必要がなくなります (大きなソース ファイルの一番下までスクロールして、どの括弧がどのスコープを閉じているかについてのコメントが欠けていた括弧を追加/削除/調整しようとしたことがありますか? 面白くない.)。

MY_COMPANY_BEGIN
MY_LIBRARY_BEGIN

class X { };

class Y { };

MY_LIBRARY_END
MY_COMPANY_END

すべての名前空間宣言を 1 行にまとめたい場合は、ちょっとした (かなり醜い) プリプロセッサ マジックを使用してそれを行うこともできます。

// helper macros for variadic macro overloading
#define VA_HELPER_EXPAND(_X)                    _X  // workaround for Visual Studio
#define VA_COUNT_HELPER(_1, _2, _3, _4, _5, _6, _Count, ...) _Count
#define VA_COUNT(...)                           VA_HELPER_EXPAND(VA_COUNT_HELPER(__VA_ARGS__, 6, 5, 4, 3, 2, 1))
#define VA_SELECT_CAT(_Name, _Count, ...)       VA_HELPER_EXPAND(_Name##_Count(__VA_ARGS__))
#define VA_SELECT_HELPER(_Name, _Count, ...)    VA_SELECT_CAT(_Name, _Count, __VA_ARGS__)
#define VA_SELECT(_Name, ...)                   VA_SELECT_HELPER(_Name, VA_COUNT(__VA_ARGS__), __VA_ARGS__)

// overloads for NAMESPACE_BEGIN
#define NAMESPACE_BEGIN_HELPER1(_Ns1)             namespace _Ns1 {
#define NAMESPACE_BEGIN_HELPER2(_Ns1, _Ns2)       namespace _Ns1 { NAMESPACE_BEGIN_HELPER1(_Ns2)
#define NAMESPACE_BEGIN_HELPER3(_Ns1, _Ns2, _Ns3) namespace _Ns1 { NAMESPACE_BEGIN_HELPER2(_Ns2, _Ns3)

// overloads for NAMESPACE_END
#define NAMESPACE_END_HELPER1(_Ns1)               }
#define NAMESPACE_END_HELPER2(_Ns1, _Ns2)         } NAMESPACE_END_HELPER1(_Ns2)
#define NAMESPACE_END_HELPER3(_Ns1, _Ns2, _Ns3)   } NAMESPACE_END_HELPER2(_Ns2, _Ns3)

// final macros
#define NAMESPACE_BEGIN(_Namespace, ...)    VA_SELECT(NAMESPACE_BEGIN_HELPER, _Namespace, __VA_ARGS__)
#define NAMESPACE_END(_Namespace, ...)      VA_SELECT(NAMESPACE_END_HELPER,   _Namespace, __VA_ARGS__)

今、あなたはこれを行うことができます:

NAMESPACE_BEGIN(Foo, Bar, Baz)

class X { };

NAMESPACE_END(Baz, Bar, Foo) // order doesn't matter, NAMESPACE_END(a, b, c) would work equally well

Foo::Bar::Baz::X x;

3 レベルより深くネストするには、必要な数までヘルパー マクロを追加する必要があります。

于 2014-12-17T19:09:10.473 に答える
13

C++ 名前空間は、インターフェイスをグループ化するために使用されますが、コンポーネントを分割したり政治的分裂を表現したりするためではありません。

この標準は、Java のような名前空間の使用を禁止しています。たとえば、ネームスペース エイリアスを使用すると、深くネストされたネームスペース名や長いネームスペース名を簡単に使用できます。

namespace a {
namespace b {
namespace c {}
}
}

namespace nsc = a::b::c;

ただし、名前空間はoriginal-namespace-namenamespace nsc {}を使用してのみ定義できるため、エラーになります。本質的に、標準はそのようなライブラリのユーザーにとって物事を容易にしますが、実装者にとっては困難です。これは、人々がそのようなことを書くことを思いとどまらせますが、書いた場合の影響を軽減します.

関連する一連のクラスと関数によって定義されるインターフェイスごとに 1 つの名前空間が必要です。内部またはオプションのサブインターフェイスは、ネストされた名前空間に入る場合があります。しかし、深さが 2 レベルを超えると、非常に深刻な危険信号となるはずです。

::演算子が不要な場合は、アンダースコア文字と識別子プレフィックスを使用することを検討してください。

于 2012-07-06T08:36:36.260 に答える
5

Lzz (Lazy C++) ドキュメントからの引用:

Lzz は、次の C++ コンストラクトを認識します。

名前空間定義

名前のない名前空間とそれに含まれるすべての宣言がソース ファイルに出力されます。このルールは、他のすべてのルールよりも優先されます。

名前付きネームスペースの名前は修飾されている場合があります。

   namespace A::B { typedef int I; }

次と同等です。

   namespace A { namespace B { typedef int I; } }

もちろん、そのようなツールに依存するソースの品質については議論の余地があります... C++ によって引き起こされる構文病が多くの形をとる可能性があることを示す、より好奇心だと思います (私も持っています...)。

于 2012-07-06T08:52:37.620 に答える
5

いいえ、そうしないでください。

名前空間の主な目的は、グローバル名前空間の競合を解決することです。

二次的な目的は、シンボルのローカル省略形です。たとえば、複雑なUpdateUIメソッドは、using namespace WndUIより短いシンボルを使用するために を使用する場合があります。

私は 1.3MLoc プロジェクトに参加しており、私たちが持っている名前空間は次のとおりです。

  • #importインポートされた外部 COM ライブラリ (主にとの間のヘッダーの競合を分離するため #include windows.h)
  • 特定の側面 (UI、DB アクセスなど) 用の 1 レベルの「パブリック API」名前空間
  • パブリック API の一部ではない「実装の詳細」名前空間 (.cpp の匿名名前空間、またはModuleDetailHereBeTygersヘッダーのみのライブラリの名前空間)
  • 列挙型は、私の経験では最大の問題です。彼らは狂ったように汚染します。
  • 名前空間が多すぎる気がする

このプロジェクトでは、クラス名などに 2 文字または 3 文字の「地域」コードを使用します (例:CDBNodeの代わりにDB::CNode)。後者を希望する場合は、2 番目のレベルの「パブリック」名前空間の余地がありますが、それ以上はありません。

クラス固有の列挙型などは、これらのクラスのメンバーになることができます(ただし、これが常に良いとは限らず、そうすべきかどうかを判断するのが難しい場合があることに同意します)

「会社」の名前空間が必要になることはめったにありませんが、バイナリとして配布されているサードパーティのライブラリに大きな問題があり、独自の名前空間を提供しておらず、簡単に名前空間に入れることができない場合を除きます (たとえば、バイナリ分布)。それでも、私の経験では、それらを名前空間に強制する方がはるかに簡単です。


[編集] Stegi のフォローアップの質問によると:

では、Win8 開発における Windows::UI::Xaml および Windows::UI::Xaml::Controls::Primitives 名前空間はどうでしょうか。Microsoft の名前空間の使用法は理にかなっており、実際には 2 レベルよりも深いものだと思います

説明不足で申し訳ありませんが、2 レベルは厳密な制限ではなく、それ以上であっても本質的に悪いわけではありません。大規模なコード ベースであっても、私の経験から、2 つ以上必要になることはめったにないことを指摘したかっただけです。ネストを深くするか浅くするかはトレードオフです。

さて、マイクロソフトのケースは間違いなく異なります。おそらく、はるかに大きなチームであり、すべてのコードはライブラリです。

Microsoft はここで .NET ライブラリの成功を模倣していると思います。そこでは、名前空間が広範なライブラリの発見可能性に貢献しています。(.NET には約 18000の型があります。)

さらに、名前空間に最適な (大きさの順序で) シンボルがあると仮定します。たとえば、1 は意味がありません。100 は正しく聞こえます。10000 は明らかに多すぎます。


TL;DR:これはトレードオフであり、明確な数字はありません。安全にプレーし、どの方向にも無理をしないでください。「それをしないでください」は単に「あなたはそれで問題がある、私はそれで問題があるだろう、そしてあなたがそれを必要とする理由がわかりません。」.

于 2012-07-06T09:00:15.773 に答える
2

両方の標準 (C++2003 と C++11) は、名前空間の名前が識別子であることを明確に示しています。これは、明示的なネストされたヘッダーが必要であることを意味します。

名前空間の単純な名前以外に修飾された識別子を配置できるようにすることは大したことではないという私の印象ですが、何らかの理由でこれは許可されていません。

于 2012-07-06T08:22:48.513 に答える
0

はい、次のようにする必要があります

namespace A{ 
namespace B{
namespace C{} 
} 
}

ただし、名前空間を想定されていない方法で使用しようとしています。この質問を確認してください。役に立つかもしれません。

于 2012-07-06T08:49:42.537 に答える