ヘッダー内のC ++でネストされた名前空間を表現するより良い方法はありますか


97

私は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

更新

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

現在、名前空間とそこにあるものの間にロジック/接着剤はありません。JavaとC#の方がはるかに優れています...なぜ<iostream>名前空間を含めて持つのstdですか?わかりました、ロジックがインクルードするためにヘッダーに依存する必要があると言うなら、なぜ#includeは、#include <std::io::stream>またはのような "IntelliSense"フレンドリーな構文を使用しないのですか?<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-結論

ほとんどの場合「それをしないでください」と言いますが、... boostでさえ、1つまたは2つのレベルよりも深い入れ子を持っています。はい、それはライブラリですが、再利用可能なコードが必要な場合-自分のコードをライブラリのように扱って、他の人に譲ってください。名前空間を使用して、検出目的でより深い入れ子を使用しています。


3
namespaceキーワードの乱用ですか?
Nawaz

4
名前空間とc#/ javaモジュールシステムは同じ目的を果たしません。そのため、同じように使用しないでください。いいえ、単純な構文はありません。単に、意図したことではないことを実行しやすくする構文を提供しても意味がないためです。
PlasmaHH 2012

@PlasmaHH ...弱点はC ++のstd libの構造化が不足していることですか?(アップデート内の私の簡単な例を参照)
Beachwalker

@Stegi:なぜそれが欠けているのか、そのような構造化からどのような確かな利点が得られるのかについて健全な議論ができるなら、潜在的な弱点について話すことができます。それまで、私はjavasをせいぜい混乱させるパッケージの無限のネストと呼んでいました。
PlasmaHH 2012

3
@PlasmaHH Intellisenseおよびその他のヘッダー(パッケージ)の後/後のヘルパー。1つの会社内の大規模なプロジェクトでは、名前空間に含まれる「スコープ」を明確に示すために、複数のネスト(vw :: golflib :: ioなど)が必要になる場合があります。まあ、vw ::を使用することもできますが、名前空間を使用して衝突を回避するのであれば、なぜ宣言するのがそれほどひどいのでしょうか。これは、誰もそれを使用しないか、深さ1の名前空間を使用するまでに終わります(多くの場合、推奨されます)。
Beachwalker

回答:


130

C ++ 17はネストされた名前空間の定義を単純化するかもしれません:

namespace A::B::C {
}

に相当

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

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


4
...コンパイラスイッチによって有効化/std:c++latest
サニームーン

3
/std:c++latestVisual Studio 2015で使用し、Boostも使用する場合、Boostヘッダーを含めると、非常に神秘的なコンパイラエラーが発生する可能性があることに注意してください。このStackOverflowの質問に
Vivit

1
名前空間A :: B :: Cのままです。私はg ++-6.0でテストしました
ervinbosenbacher


17

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

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

#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レベルより深くネストする場合は、目的の数までヘルパーマクロを追加する必要があります。


私が#defines を嫌うのと同じくらい、そのプリプロセッサの魔法にはかなり感動します...より深い入れ子にするためにヘルパーマクロを追加する必要がない場合のみ...まあ、とにかくそれを使用するつもりはありません。 。
galdin

12

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

この標準は、Javaのような名前空間の使用を禁止するものではありません。たとえば、名前空間エイリアスは、深くネストされた、または長い名前空間名を簡単に使用する方法を提供します。

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

namespace nsc = a::b::c;

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

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

::演算子が不要な場合は、アンダースコア文字と識別子の接頭辞の使用を検討してください。


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

2
2未満のレベルを使用することは危険であり、3または4を使用することは完全に問題ありません。名前空間が意味をなさないときにフラットな名前空間階層を実現しようとすると、名前空間の目的そのものが無効になり、名前の衝突が回避されます。インターフェイスに1つのレベルを、サブインターフェイスと内部にもう1つのレベルを設定することに同意します。しかし、その周りには、会社の名前空間(中小規模の会社の場合)または会社と部門(大企業の場合)の少なくとも2つのレベルが必要です。そうしないと、インターフェースの名前空間が、他の場所で開発された同じ名前を持つ他のインターフェースの名前空間と
競合

@Kaiserludi company::division以上の技術的な利点は何company_divisionですか?
Potatoswatter

@Potatoswatter Inside company :: anotherDivsionでは、より短い「区分」を使用できます。「using namespace」を使用して上位レベルのネームスペースを汚染しないようにする必要があるヘッダー内でも、company :: divisionを参照してください。会社の名前空間の外でも、「名前空間の会社を使用する」ことができます。部門名がスコープ内の他のどの名前空間とも衝突しないが、一部の部門名前空間内のインターフェース名が衝突するため、「using namespace company_division;」を実行できない場合。
カイザールディ

3
@Potatoswatterポイントは、実質的に無料で入手でき(company :: divisionはcompany_divisionより長くない)、それを使用するために最初に追加の名前空間エイリアスを定義する必要がないことです。
カイザールディ

6

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

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

二次的な目的は、シンボルのローカル省略形です。たとえば、複雑なUpdateUIメソッドはusing namespace WndUIを使用して短いシンボルを使用できます。

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

  • 主との間の単離ヘッダ競合にインポートされた外部のCOMライブラリ( #import及び#include windows.h
  • 特定の側面(UI、DBアクセスなど)の「パブリックAPI」名前空間の1つのレベル
  • パブリック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:これはトレードオフであり、具体的な数値はありません。安全にプレーし、どの方向にも無理をしないでください。「それをしないでください」は単に「あなたはそれで問題があります、私はそれで問題があります、そしてあなたがそれを必要とする理由がわかりません。」から単に来ます。


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

2
グローバルアクセス定数が必要な場合は、それらをのような名前の名前空間に配置しConstants、定数を分類するために適切な名前でネストされた名前空間を作成します。必要に応じて、名前空間を使用して名前の衝突を防ぎます。このConstants名前空間自体は、プログラムのシステムコードの包括的な名前空間に含まれ、などの名前が付けられていSysDataます。これは、3つのまたは4つの名前空間を含む完全修飾名(例えば、作成SysData::Constants::ErrorMessagesSysData::Constants::Ailments::BitflagsまたはSysData::Defaults::Engine::TextSystem)。
ジャスティン時間-モニカを

1
実際のコードで定数が必要な場合、定数を必要とする関数はusingディレクティブを使用して適切な名前を取り込み、名前の競合の可能性を最小限に抑えます。読みやすさが向上し、特定のコードブロックの依存関係を文書化するのに役立ちます。別に定数から、私がしようとする傾向があり、可能な場合に(のような2つの名前空間にそれを維持SysData::ExceptionsしてSysData::Classes)。
ジャスティンタイム-モニカ

2
全体として、一般的なケースでは、入れ子になった名前空間の数を最小限に抑えるのが最善ですが、何らかの理由でグローバルオブジェクトが必要な場合(定数または変更可能、できれば前者)、複数の入れ子の名前空間を使用する必要がありますそれらを適切なカテゴリに分類し、使用方法を文書化し、潜在的な名前の衝突を最小限に抑えます。
ジャスティン時間-モニカを

2
理由は客観的な理由なしに「これを行わないでください」の場合は-1 (後で説明しますが)。言語はネストされた名前空間をサポートしており、プロジェクトにはそれらを使用する十分な理由がある場合があります。そのような考えられる理由とそうすることに対する具体的で客観的な欠点についての議論は、私の反対投票を逆転させるでしょう。
TypeIA 2017年

4

Lzz(レイジーC ++)ドキュメントからの引用:

Lzzは、次のC ++構成を認識します。

名前空間の定義

名前のない名前空間とすべての囲まれた宣言は、ソースファイルに出力されます。このルールは他のすべてをオーバーライドします。

名前付き名前空間の名前は修飾できます。

   namespace A::B { typedef int I; }

以下と同等です。

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

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


2

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

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


1

次の構文を使用できます。

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

// Here is where the magic happens
class MyCompany::MyModule::MyModulePart::MySubModulePart::MyYouGetTheIdeaModule::MyClass {
    ...
};

この構文はC ++ 98でも有効であり、ネストされた名前空間定義を使用してC ++ 17で現在使用できる構文とほとんど同じです。

ネストを解除してください。

出典:


それは、より良い解決策が代わりに検索される質問で言及された構文です。現在、C ++ 17は、受け入れられた回答で述べられているように、有効な代替手段として利用できます。申し訳ありませんが、質問と回答を読んでいないことに反対票を投じてください。
ビーチウォーカー、

@Beachwalkerは構文にとらわれないようにしましょう。上記の名前空間宣言も、受け入れられた回答と同じである可能性があります。この回答で強調したい主なポイントは、OPが見逃したと言ったことと、その名前空間の混乱の下で私がやったことです。私が見る限り、誰もが名前空間内のすべてを宣言することに集中しているようですが、私の答えはネストされた混乱からあなたを連れて行き、OPがこの構文を4年前に誰かが言及したときにこの構文を高く評価したと確信しています最初に尋ねられました。
smac89

1

この論文は、主題をかなりうまくカバーしています:名前空間論文

これは基本的にこれを煮詰めたものです。名前空間が長いほど、人々がusing namespaceディレクティブを使用する可能性が高くなります。

したがって、次のコードを見ると、これが害を及ぼす例がわかります。

namespace abc { namespace testing {
    class myClass {};
}}

namespace def { namespace testing {
    class defClass { };
}}

using namespace abc;
//using namespace def;

int main(int, char**) {
    testing::myClass classInit{};
}

このコードは正常にコンパイルされますが、行のコメントを外すと//using namespace def;、「testing」名前空間があいまいになり、名前の衝突が発生します。これは、サードパーティのライブラリを含めることにより、コードベースが安定から不安定に移行する可能性があることを意味しています。

C#では、使用using abc;using def;てコンパイラがそれを認識できるtesting::myClass場合でもmyClassabc::testing名前空間にのみ存在する場合でも、C ++はこれを認識せず、衝突として検出されます。


0

はい、あなたはそれをする必要があります

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

ただし、名前空間を使用することを想定していない方法で使用しようとしています。この質問をチェックしてください。多分あなたはそれが役に立つでしょう。


-1

[編集:]
c ++ 17以降、ネストされた名前空間は標準言語機能としてサポートされています(https://en.wikipedia.org/wiki/C%2B%2B17)。現在、この機能はg ++ 8ではサポートされていませんが、clang ++ 6.0コンパイラで見つけることができます。


[結論:]デフォルトのコンパイルコマンドとして
使用clang++6.0 -std=c++17します。その後、すべてが正常に動作するはずnamespace OuterNS::InnerNS1::InnerNS2 { ... }です。ファイルでコンパイルできます。


[元の
回答:]この質問は少し古いので、先に進んだと思います。しかし、まだ答えを探している他の人のために、私は次のアイデアを思いつきました:

メインファイル、名前空間ファイル、コンパイルコマンド/結果、およびコマンドライン実行を示すEmacsバッファー。

(私はここでEmacsの広告を作成するかもしれません:)?)画像を投稿することは、単にコードを投稿することよりもはるかに簡単で読みやすくなっています。私はすべてのコーナーケースの本格的な回答を提供するつもりはありません。単にインスピレーションを与えるつもりでした。(私はC#を完全にサポートしており、C#は主にその使いやすさの比較から人気があるため、多くの場合、C ++はいくつかのOOP機能を採用するべきだと感じています)。


更新:C ++ 17にはネストされた名前空間(nuonsoft.com/blog/2017/08/01/c17-nested-namespaces)があるため、古いバージョンのC ++を使用している場合を除いて、私の答えは関係がないように思われます。 。
ワイキんぐ

1
私がEmacsを愛しているのと同じくらい、画像を投稿することは理想的ではありません。テキスト検索/インデックス作成を回避し、視覚障害のある訪問者があなたの答えにアクセスするのを困難にします。
ハインリヒは
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.