.cppファイルでC ++名前空間メソッドを定義する正しい方法


108

おそらく重複していますが、簡単に検索することはできません...

次のようなヘッダーがあるとします。

namespace ns1
{
 class MyClass
 {
  void method();
 };
}

私はmethod().cppファイルでいくつかの方法で定義されているのを見ました:

バージョン1:

namespace ns1
{
 void MyClass::method()
 {
  ...
 }
}

バージョン2:

using namespace ns1;

void MyClass::method()
{
 ...
}

バージョン3:

void ns1::MyClass::method()
{
 ...
}

それを行う「正しい」方法はありますか?これらはすべて同じ意味ではないという点で「間違った」ものはありますか?


ほとんどのコードで通常3番目のバージョンが表示されます(ただし、なぜ:Dではありません)。2番目のバージョンは、名前空間が導入された理由とは正反対です。
Mr.Anubis 2011

1
興味深い事実のように、Visual Assistリファクタリングツールは、ターゲットファイルですでに使用されているスタイルに応じて、#1または#3を使用してコードを生成します。
氏Mr. Boy

回答:


51

バージョン2は不明確であり、理解するのは簡単ではありません。これMyClassは、どの名前空間が属しているかがわからず、非論理的だからです(クラス関数が同じ名前空間にないのですか?)

バージョン1が正しいのは、名前空間で関数を定義していることを示しているためです。

::スコープ解決演算子を使用MyClass::method ()して名前空間のを参照しているため、バージョン3も正しいですns1。私はバージョン3を好みます。

名前空間(C ++)を参照してください。これはこれを行うための最良の方法です。


22
#2を「間違った」と呼ぶのは、非常に誇張されています。このロジックでは、他のスコープ内の他のシンボル名を潜在的に隠す可能性があるため、すべてのシンボル名は「間違っています」。
tenfour

それは非論理的です。それはうまくコンパイルされます(申し訳ありませんが、答えで誤解されています)が、なぜ名前空間の外で関数を定義するのですか?読者を混乱させます。また、多くの名前空間が使用されている場合、MyClassがどの名前空間に属しているかは表示されません。バージョン1と3では、この問題が修正されています。結論として、それは間違いではなく、不明確で混乱を招くだけです。
GILGAMESH 2011

3
@PhoenicaMaciaに同意します。使用するトリックはひどく、混乱を招く可能性があります。演算子をフリー関数として実装するクラスを考えてみましょう。ヘッダーにありますがnamespace N {struct X { void f(); }; X operator==( X const &, X const & ); }、現在はcppファイルでusingステートメントを使用してメンバー関数をとしてvoid X::f() {}定義X operator==(X const&, X const&)できますが、定義すると、別の演算子を定義することになりますヘッダーで定義されています(無料の関数には1または3を使用する必要があります)。
DavidRodríguez-

1
特に、私は1を好んでおり、リンクされた記事の例は、最初の例が1を使用するものを実際には解決しません、2番目は1と3の混合を使用します(関数は修飾で定義されますがそれらは外部名前空間内で定義されます)
DavidRodríguez-

1
3つのうちの1つは1)が最高だと思いますが、ほとんどのIDEはその名前空間宣言内のすべてをインデントするというやっかいな癖があり、混乱を招きます。
ロッカ2014

28

5年後、私はこれに言及したいと思いました、それはどちらも見た目が良く、邪悪ではありません

using ns1::MyClass;

void MyClass::method()
{
  // ...
}

3
これが最良の答えです。これは見た目がすっきりしており、OPのバージョン1の問題を回避します。これにより、意図せずに名前空間に物事が持ち込まれ、2が意図せずにグローバルスペースに物事が持ち込まれる可能性があります。
ayane_m 2016年

はい、これはタイピングが3より少ないという優れた組み合わせですが、意図を明示的に宣言します。
jb

14

私はバージョン4(以下)を使用しています。バージョン1(resoective定義の簡潔さ)とバージョン3(最大限に明示的)の利点のほとんどを組み合わせているためです。主な欠点は、人々がそれに慣れていないことですが、私はそれが技術的に他の選択肢より優れていると思うので、気にしません。

バージョン4:名前空間エイリアスを使用して完全修飾を使用します。

#include "my-header.hpp"
namespace OI = outer::inner;
void OI::Obj::method() {
    ...
}

私の世界では、すべてが明示的に修飾されている(変数名など)か、既知のカスタマイズポイント(関数テンプレートのswap()など)でない限り、名前空間エイリアスを頻繁に使用しています。


1
「人を混乱させてもかまわない」という方がいいと思うロジックには欠陥あります、これネストされた名前空間に対する優れたアプローチであることに同意する必要あります。
氏Mr. Boy

1
「なぜ私はそれを考えなかったのか」という優れたアイデアの+1!(「人々は[技術的に優れた新しいもの]に慣れていない」と同様に、より多くの人々がそれをやれば、慣れるでしょう。)
wjl

念の私は、両方されて理解させるにするouterinner、既に他のヘッダファイルで名前空間として定義されますか?
dekuShrub

4

バージョン3では、クラスと名前空間の間の関連付けが、より多くのタイピングを犠牲にして非常に明示的になっています。バージョン1はこれを回避しますが、ブロックとの関連付けをキャプチャします。バージョン2はこれを隠す傾向があるので、私はそれを避けます。



3

私はNum.3(別名詳細バージョン)を選択します。それはよりタイピングですが、意図はあなたとコンパイラに正確です。あなたが現状で投稿した問題は、実際には現実の世界よりも単純です。現実の世界では、クラスメンバーだけでなく、定義のスコープが他にもあります。ネームスペースやグローバルスコープなどとは異なり、スコープが再度開かれることがないため、クラスだけで定義が複雑になることはありません。

Num.1これはクラス以外のスコープで失敗する可能性があります-再開できるものなら何でも。したがって、このアプローチを使用して名前空間で新しい関数を宣言するか、インラインがODRを介して置き換えられる可能性があります。これは、一部の定義(特に、テンプレートの特殊化)で必要になります。

Num.2これは非常に壊れやすく、特に大規模なコードベースでは、ヘッダーと依存関係がシフトするため、プログラムのコンパイルに失敗します。

Num.3これは理想的ですが、タイプすることはたくさんあります-あなたの意図は何かを定義することです。これはまさにそれを行い、コンパイラはあなたが間違いを犯していないか、定義がその宣言と同期していないなどを確認するために起動します。



2

すべての方法が正しく、それぞれに長所と短所があります。

バージョン1では、各関数の前に名前空間を記述する必要がないという利点があります。欠点は、特に複数のレベルの名前空間がある場合は、退屈なIDを取得することです。

バージョン2では、コードをよりクリーンにしますが、CPPに実装されている名前空間が複数ある場合、他の名前空間の関数と変数に直接アクセスして、名前空間を(そのcppファイルに対して)使用できなくする可能性があります。

バージョン3では、さらに入力する必要があり、関数の行が画面よりも大きくなる可能性があり、デザイン効果には良くありません。

一部の人々はそれを使用する別の方法もあります。これは最初のバージョンと似ていますが、識別の問題はありません。

こんな感じです:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 }

OPEN_NS1

void MyClass::method()
{
...
}

CLOSE_NS1

状況に応じてどちらを選ぶかはあなた次第です=]


14
ここでマクロを使用する理由はわかりません。インデントしたくない場合は、インデントしないでください。マクロを使用すると、コードがわかりにくくなります。
tenfour

1
名前空間をサポートしない古いコンパイラでコードをコンパイルしたいときはいつでも、あなたが言及した最後のバージョンが役立つと思います(はい、恐竜はまだ残っています)。その場合、マクロを#ifdef句の中に置くことができます。
Luca Martini

必要がない場合は特定する必要はありませんが、マクロを使用しない場合は、一部のIDEがそれを試みます。たとえば、Visual Studioでは、コード全体を選択し、Alt + F8キーを押して自動識別できます。定義を使用しない場合、その機能は失われます。また、OPEN_(namespace)とCLOSE_(namespace)がコーディング標準に含まれている場合は、それほど明白ではないと思います。@LucaMartiniが与えた理由も興味深いです。
Renan Greinert、2011

これが一般的になった場合、つまり、少し便利#define OPEN_NS(X)だと思いますが、実際にはそうではありません...マクロには反対しませんが、これは少しOTTのようです。ネストされた名前空間には、DietmarKühlのアプローチの方が優れていると思います。
Boy氏
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.