web-dev-qa-db-ja.com

std名前空間の使用

Std名前空間に関して「使用」を使用する上で、異なる見解があるようです。

using namespace std」を使用すると言う人もいれば、「std::」で使用するstd関数にプレフィックスを付けないで言う人もいれば、次のようなものを使用すると言う人もいます。

using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;

使用されるすべてのstd関数。

それぞれの長所と短所は何ですか?

102
paoloricardo

ほとんどのC++ユーザーはstd::stringstd::vectorなどを読んで非常に満足しています。実際、生のvectorを見ると、これがstd::vectorなのか、別のユーザー定義のvectorなのか疑問に思います。

私はいつもusing namespace std;を使うことに反対です。あらゆる種類の名前をグローバル名前空間にインポートし、あらゆる種類の非自明なあいまいさを引き起こす可能性があります。

std名前空間にあるいくつかの一般的な識別子は次のとおりです:count、sort、find、equal、reverse。 countというローカル変数があると、using namespace stdではstd::countの代わりにcountを使用できなくなります。

不要な名前の競合の典型的な例は、次のようなものです。あなたが初心者で、std::countについて知らないことを想像してください。 <algorithm>で他の何かを使用しているか、または一見無関係なヘッダーによって引き込まれていると想像してください。

#include <algorithm>
using namespace std;

int count = 0;

int increment()
{
    return ++count; // error, identifier count is ambiguous
}

std::countはいくつかの長いネストされた型を持つテンプレートであるため、エラーは一般的に長くてわかりにくいものです。

ただし、std::countはグローバルネームスペースに入り、関数カウントによって非表示になるため、これは問題ありません。

#include <algorithm>
using namespace std;

int increment()
{
    static int count = 0;
    return ++count;
}

おそらく少し驚いたことに、これで問題ありません。宣言スコープにインポートされた識別子は、定義された場所とインポートされた場所の両方を囲む共通のネームスペースに表示されます。つまり、std::countは、グローバル名前空間ではcountとして表示されますが、increment内でのみ表示されます。

#include <algorithm>

int increment()
{
    using namespace std;
    static int count = 0;
    return ++count;
}

同様の理由で、ここではcountがあいまいです。 using namespace stdstd::countを引き起こさず、予想されるように外側のcountを非表示にします。 using namespaceルールは、std::countが(increment関数内で)グローバルスコープ、つまりint count = 0;と同じスコープで宣言されているように見えるため、あいまいさを引き起こすことを意味します。

#include <algorithm>

int count = 0;

int increment()
{
    using namespace std;
    return ++count; // error ambiguous
}
125
CB Bailey

基本を除外(すべてのstlオブジェクト/関数の前にstd ::を追加し、「名前空間stdを使用」していない場合の競合の可能性を減らします)

また、絶対に入れないでください

using namespace std

ヘッダーファイルでは、その名前空間を使用したくない場合でも、そのヘッダーファイルを含むすべてのファイルに伝播できるため。

場合によっては、次のようなものを使用することが非常に有益です

using std::swap

特別なバージョンのスワップがあるかのように、コンパイラはそれを使用します。そうでない場合、std :: swapにフォールバックします

Std :: swapを呼び出すと、常に基本バージョンが使用され、最適化バージョン(存在する場合)は呼び出されません。

40
Yacoby

まず、いくつかの用語:

  • using-declaration:_using std::vector;_
  • using-directive:_using namespace std;_

sing-directivesを使用しても、ヘッダーファイルのグローバルスコープで使用されない限り問題ありません。だから

_using namespace std;
_

.cppファイルの問題は実際には問題ではありません。問題が見つかった場合は、完全に管理下にあります(必要に応じて特定のブロックにスコープすることもできます)。多数の_std::_修飾子でコードを乱雑にする特別な理由はありません-それは単なる視覚的なノイズの塊になります。ただし、コードでstd名前空間の名前の束全体を使用していない場合、ディレクティブを省略しても問題はありません。これはトートロジーです-ディレクティブが不要な場合は、使用する必要はありません。

同様に、std名前空間の特定のタイプに対してsing-declarations(代わりにsing-directivesの代わりに)を使用して取得できる場合、これらの特定の名前だけを現在のネームスペースに持ち込むべきではない理由。同じように、単一のusingディレクティブでも同様にトリックを実行できる場合、25または30のusing宣言を使用するのは面倒で、簿記の手間がかかると思います。

また、mustでusing宣言を使用する必要がある場合があることに留意してください。効果的なC++、第3版のScott Meyersの「項目25:スローしないスワップのサポートを検討する」を参照してください。汎用のテンプレート関数でパラメーター化された型に「最適な」スワップメソッドを使用するには、使用宣言と引数依存のルックアップ(別名ADLまたはKoenigルックアップ)を使用する必要があります。

_template< typename T >
void foo( T& x, T& y)
{
    using std::swap;     // makes std::swap available in this function

    // do stuff...

    swap( x, y);         // will use a T-specific swap() if it exists,
                         //  otherwise will use std::swap<T>()

    // ...
 }
_

名前空間を大幅に使用するさまざまな言語の一般的なイディオムを検討する必要があると思います。たとえば、JavaおよびC#は大部分(おそらくC++よりも)名前空間を使用します。名前空間内の名前がこれらの言語で使用される最も一般的な方法は、現在のスコープにまとめて移動することです。これは広範囲に及ぶ問題を引き起こさず、問題である数回は、完全修飾名またはエイリアスによって問題の名前を処理することにより、「例外」ベースで処理されます。 -C++でできるように。

Herb SutterとAndrei Alexandrescuは、本の「項目59:ヘッダーファイルまたは#includeの前に名前空間を使用しないでください」、C++コーディング標準:101ルール、ガイドライン、およびベストプラクティスで次のように述べています。

つまり、実装ファイルで_#include_ディレクティブの後に宣言とディレクティブを自由に使用して名前空間を使用できます。反対の主張が繰り返されるにもかかわらず、宣言とディレクティブを使用する名前空間は悪ではなく、名前空間の目的を無効にしません。むしろ、それらは名前空間を使用可能にするものです。

Stroupstrupは、「The C++ Programming Language、Third Edition」で「グローバルネームスペースを汚染しないでください」と言われることがよくあります。実際、彼はそれを言っています(C.14 [15])が、C.10.1章では次のように述べています。

using-declarationは、ローカルスコープに名前を追加します。 using-directiveはありません;宣言されたスコープ内で名前にアクセスできるようにするだけです。例えば:

_namespaceX {
    int i , j , k ;
}

int k ;
void f1()
{
    int i = 0 ;

    using namespaceX ; // make names from X accessible

    i++; // local i
    j++; // X::j
    k++; // error: X::k or global k ?

    ::k ++; // the global k

    X::k ++; // X’s k
}

void f2()
{
    int i = 0 ;

    using X::i ; // error: i declared twice in f2()
    using X::j ;
    using X::k ; // hides global k

    i++;
    j++; // X::j
    k++; // X::k
}
_

ローカルで宣言された名前(通常の宣言またはusing宣言によって宣言された)は、同じ名前の非ローカル宣言を隠し、名前の不正なオーバーロードは宣言の時点で検出されます。

f1()の_k++_のあいまいさエラーに注意してください。グローバル名は、グローバルスコープでアクセス可能になった名前空間の名前よりも優先されません。これは、偶発的な名前の衝突に対する重要な保護を提供し、重要なことには、グローバル名前空間を汚染することから得られる利点がないことを保証します。

Usingディレクティブを使用して多くの名前を宣言するライブラリにアクセスできるようにすると、未使用の名前の衝突がエラーと見なされないという大きな利点があります。

...

従来のCおよびC++プログラムと比較して、名前空間を使用する新しいプログラムでのグローバル名の使用が根本的に減少することを期待しています。名前空間のルールは、グローバルスコープを汚染しないように注意を払う人よりも、グローバル名の「怠 ’な」ユーザーに利点を与えないように特別に作成されました。

そして、どのように「グローバル名の怠lazなユーザー」と同じ利点がありますか? using-directiveを利用することにより、これはsafelyネームスペースの名前を現在のスコープで利用可能にします。

区別があることに注意してください-std名前空間の名前は、usingディレクティブを適切に使用してスコープで使用可能になります(_#includes_の後にディレクティブを配置することでnotグローバル名前空間を汚染します。これらの名前を簡単に利用できるようにし、衝突から保護し続けています。

26
Michael Burr

ヘッダーファイルのグローバルスコープでネームスペースを使用しないでください。これにより競合が発生する可能性があり、競合が発生したファイルの担当者は原因を制御できません。

実装ファイルでは、選択肢がはるかに少なくなっています。

  • Using namespace stdを置くと、その名前空間のすべてのシンボルが表示されます。これは、追加されるシンボルについて話すことなく、ほとんどの人がそこにあるすべてのシンボルを知らないので、面倒な場合があります(したがって、競合のないポリシーを実際に適用することは不可能です)。また、C++標準では、ヘッダーで他のヘッダーからシンボルを追加できます(Cのヘッダーでは許可されていません)。制御されたケースでの記述を簡素化するために、実際にはうまく機能します。また、エラーが発生した場合は、問題のあるファイルで検出されます。

  • Std :: nameを使用した書き込み。未知のシンボルをインポートするリスクなしに書くのが簡単であるという利点があります。コストは、必要なすべてのシンボルを明示的にインポートする必要があることです。

  • 明示的に修飾することで少し混乱が生じますが、練習することで問題が少なくなると思います。

私のプロジェクトでは、すべての名前に明示的な修飾を使用し、std :: nameの使用を受け入れ、名前空間stdの使用と戦います(独自のリストタイプを持つLISPインタープリターがあるため、競合は確実です)。

他の名前空間については、使用される命名規則も考慮する必要があります。名前空間(バージョン管理用)と名前のプレフィックスを使用するプロジェクトを知っています。 _using namespace X_を実行してもほとんどリスクはありません。実行しないと、愚かなコードPrefixNS::pfxMyFunction(...)になります。

シンボルをインポートしたい場合があります。 std :: swapが最も一般的なケースです。std:: swapをインポートしてから、未修飾のswapを使用します。引数に依存するルックアップは、タイプの名前空間に適切なスワップがある場合はそれを見つけ、ない場合は標準テンプレートにフォールバックします。


編集:

コメントでは、Michael Burrは、競合が現実の世界で発生するかどうか疑問に思います。これが実際のライブ例です。拡張言語にはLISP方言があります。インタープリターには、LISP.hを含むインクルードファイルがあります。

_typedef struct list {} list;
_

次のようなコード(これを「エンジン」と呼びます)を統合して適応させる必要がありました。

_#include <list>
...
using std::list;
...
void foo(list const&) {}
_

そこで、次のように変更しました。

_#include <list>

#include "module.h"
...
using std::list;
...
void foo(list const&) {}
_

良い。すべてが機能します。数か月後、「module.h」が「list.h」を含むように変更されました。テストに合格しました。 「モジュール」は、そのABIに影響するような方法で変更されていなかったため、「エンジン」ライブラリは、ユーザーを再コンパイルせずに使用できます。統合テストは問題ありませんでした。新しい「モジュール」が公開されました。エンジンの次のコンパイルは、そのコードが変更されていないときに壊れました。

17
AProgrammer

両方

using std::string;

そして

using namespace std;

いくつかのシンボル(1つまたは多数)をグローバル名前空間に追加します。そして、グローバル名前空間にシンボルを追加することは、あなたがすべきことですneverヘッダーファイルで行う。ヘッダーを含めるユーザーを制御することはできません。他のヘッダーを含むヘッダー(およびヘッダーを含むヘッダーを含むヘッダーなど)が多数あります。

実装(.cpp)ファイルでは、それはあなた次第です(忘れないでくださいafterすべての#includeディレクティブ)。この特定のファイル内のコードのみをブレークできるため、名前の競合の原因を管理および発見するのが簡単になります。識別子の前にstd ::(または他のプレフィックス、プロジェクトに多くの名前空間が存在する可能性があります)を使用する場合は、問題ありません。使用する識別子をグローバル名前空間に追加する場合は、問題ありません。名前空間全体を頭に載せたい場合は:-)、それはあなた次第です。効果は単一のコンパイル単位に制限されていますが、許容範囲です。

4
Tadeusz Kopec

私にとっては、:: 可能であれば。

std::list<int> iList;

私は書くのが嫌い:

for(std::list<int>::iterator i = iList.begin(); i != iList.end(); i++)
{
    //
}

うまくいけば、C++ 0xでこれを書きます。

for(auto i = iList.begin(); i != iList.end(); i++)
{
    //
}

名前空間が非常に長い場合、

namespace dir = boost::filesystem;

dir::directory_iterator file("e:/boost");
dir::directory_iterator end;

for( ; file != end; file++)
{
    if(dir::is_directory(*file))
        std::cout << *file << std::endl;
}
3
AraK

コード内でstdおよび他のライブラリと名前の競合のリスクがない場合は、次を使用できます。

using namespace std;

しかし、ドキュメントのコードの依存関係を正確に知りたい場合、または名前の競合のリスクがある場合は、他の方法を使用します。

using std::string;
using std::cout;

3番目の解決策は、これらの解決策を使用せずにstd:を記述します。コードを使用するたびにセキュリティが向上する前に、コードが少し重くなるかもしれません...

3
Matthieu

ヘッダーの名前空間スコープで_using namespace std_であってはなりません。また、ほとんどのプログラマーは_std::_なしでvectorまたはstringを見ると不思議に思うので、_using namespace std_の方が良いとは思わないでしょう。したがって、決して_using namespace std_になることはないと主張します。

必要に応じて、_using std::vector_などの宣言を使用してローカルを追加します。しかし、自問してみてください:この価値は何ですか? 1行のコードは1回(おそらく2回)書き込まれますが、10、100、または1000回読み取られます。 using宣言またはディレクティブを追加することで節約できるタイピングの労力は、コードを読み取る労力と比較してわずかです。

それを念頭に置いて、10年前のプロジェクトで、すべての識別子を完全な名前空間名で明示的に修飾することにしました。最初はぎこちないように思えたものが、2週間以内に日常的になりました。現在、その会社全体のすべてのプロジェクトで、ディレクティブや宣言を使用しているユーザーはいません。 (1つの例外を除き、以下を参照してください。)10年後のコード(複数のMLoC)を見ると、適切な決定を下したように感じます。

通常、usingの禁止に反対する人は、通常、1つのプロジェクトでそれを試していないことがわかりました。試した人は、非常に短い時間の後にディレクティブ/宣言を使用するよりも、多くの場合それを見つけます。

注:唯一の例外は_using std::swap_であり、これはstd名前空間に入れることができないswap()のオーバーロードを取得するために必要です(特に汎用コードでは) std関数のオーバーロードをこの名前空間に入れることができます)。

2
sbi

名前空間は、関数シグネチャの confusion および pollution を防ぐためにコードを保持します。

ここ は、ドキュメント化された完全なデモです 適切な名前空間 使用法:

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see https://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace

// Silently overrides std::log
//double log(double d) { return 420; }

namespace uniquename {
    using namespace std;  // So we don't have to waste space on std:: when not needed.

    double log(double d) {
        return 42;
    }

    int main() {
        cout << "Our log: " << log(4.2) << endl;
        cout << "Standard log: " << std::log(4.2);
        return 0;
    }
}

// Global wrapper for our contained code.
int main() {
    return uniquename::main();
}

出力:

Our log: 42
Standard log: 1.43508
1
Cees Timmerman

using namespace stdは、現在の名前空間のstd名前空間の内容をインポートします。したがって、その名前空間のすべての関数の前にstd::と入力する必要がないという利点があります。ただし、同じ名前の関数を持つ異なる名前空間がある場合があります。したがって、必要なものを呼び出さないで終了する場合があります。

stdにインポートするものを手動で指定すると、それが起こらないようにできますが、ファイルの先頭で長いリストが使用される可能性があり、開発者によっては見苦しいと思われます;)!

個人的には、名前空間が長すぎる場合を除き、関数を使用するたびに名前空間を指定することを好みます。その場合、ファイルの先頭に一部を使用します。

編集:別の回答に記載されているように、ヘッダーファイルにusing namespaceを配置しないでください。

EDIT2:チャールズのコメントのおかげで私の答えを修正しました。

1
Wookai

この議論は、作業するIDEがあなたが必要とする正確な情報を表示したり隠したりするのに十分な柔軟性がない限り、生き続けます。

それは、コードをどのように見せたいかは、手元のタスクに依存するからです。

ソースコードの作成中に、使用しているクラスを正確に確認したい:std::string、 または BuzFlox::Obs::stringクラス?

制御フローを設計するとき、私は変数の型にさえ興味がありませんが、ifwhilecontinueに焦点を合わせたいです。

だからこれは私のアドバイスです:

コードの対象者とツールの能力に応じて、最も読みやすい方法またはほとんどの情報を提供する方法を選択します。

0
xtofl

Javaのように、Java.util。*をインクルードするか、単に各クラスを個別に選択することができます。スタイルに依存します。1つは必要ないことに注意してくださいusing namespace stdは、ファイル/ワイドスコープの先頭にあります。これは、名前空間を汚染し、場合によっては衝突して名前空間のポイントを無効にするためです。しかし、多くのSTLを使用する関数がある場合、ロジックが混乱してプレフィックスの構文がコードに散らかるので、おそらくusing namespace std(さまざまなクラスを使用する場合)または個々のusings(いくつかのクラスを頻繁に使用する場合)。

0
Sam Brightman

これを修正する方法はいくつかあります。

最初:あなたがしたように使用します。

2番目:namespace S = std;、2文字削減します。

3番目:staticを使用します。

4番目:stdが使用する名前を使用しないでください。

0
user5058091