C++ヘッダーファイルでは、次のように名前空間内でusing宣言を使用しても安全(かつ正しい)ですか。
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
using boost::numeric::ublas::vector;
vector MyFunc(vector in);
}
つまり「using boost :: numeric :: ublas :: vector」はMyNamespaceブロック内に適切に含まれていますか、それともこのヘッダーを含むファイルの名前空間を汚染しますか?
いいえ、安全ではありません。別の名前空間を汚染することはありませんが、他の理由で危険です。
using
ディレクティブは、現在表示されているものを、指定した名前で使用するネームスペースにインポートします。 using
はMyNamespace
のユーザーにのみ表示されますが、「外部」からの他のものはusing
宣言に表示されます。
では、ヘッダーで使用すると、どのように危険ですか?宣言の時点で表示されるものをインポートするため、正確な動作は、宣言の前に含めるヘッダーの順序によって異なります(boost::numeric::ublas::vector
から表示されるものは異なる場合があります)。ヘッダーの前にどのヘッダーを含めるかを実際に制御することはできないので(そうするべきではありません!ヘッダーは自己完結型でなければなりません!)、これは非常に奇妙な問題を引き起こし、関数は1つのコンパイルユニットと別のコンパイルユニットにあるものを見つけます。次。
経験則として、using
宣言は、すべての.cppファイルにインクルードした後にのみ使用する必要があります。 SutterとAlexandrescuによる本「C++ Coding Standards」(Item 59)にも、この正確な問題に関する項目があります。 「しかし、ここに共通の罠があります。名前空間レベル(...)で発行された宣言を使用することは安全であると多くの人が考えています。安全ではありません。少なくとも同じくらい危険で、より微妙でより油断な方法です。」
あなたがusing
である名前が他にどこにも存在しない可能性が低い場合でも(おそらくここではそうです)、物事は醜くなります:ヘッダーでは、すべての宣言は完全に修飾されています。これは苦痛ですが、それ以外の場合、奇妙なことが発生する可能性があります。
例と詳細に説明されている問題については、 名前空間への移行 、 使用宣言と名前空間エイリアス および 名前空間の命名 も参照してください。
使用宣言は、名前が示すように、宣言です。すべての宣言のスコープは、包含ブロック(7.2)です。この場合、名前空間はMyNamespace
です。その名前空間の外には表示されません。
安全ですが、MyNamespace名前空間を汚染します。したがって、そのヘッダーを含むファイルには、MyNamespaceに関数/クラスが含まれます。
要約すると、no、ヘッダーのusing宣言はnot ok 、名前空間内であっても、2つの理由があります。さらに、非ヘッダーの名前空間内でのusing宣言は、エラーが発生しやすい、または無意味です(末尾を参照)。ヘッダー内のusing宣言は、次の理由で問題があります。
あなたの例では、これは次のことを意味します:
MyNamespace
内で、vector
は、このヘッダーを含むすべてのファイルでboost::numeric::ublas::vector
に解決される可能性があります。これは、MyNamespace
名前空間を「汚染」します。boost::numeric::ublas::vector
宣言は、どの宣言が表示されるかによって異なりますbeforeこのusing宣言は、インクルードの順序によって異なりますこのヘッダーを含むファイル内、およびitsinclude(正しくは、前処理後の翻訳単位での宣言の順序)。あたり 11月51日のMay 30 '11のコメント 実際には動作1が必要ですが、問題2のためにこれは機能しません。別のヘッダーを使用すると、目的の動作を得ることができます。他のすべての後に含まれます(他のヘッダーの名前を完全に修飾します)。ただし、これは脆弱であるためお勧めできません。名前空間に移行するときにのみ予約することをお勧めします。
//--- file myheader.hpp ---
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
::boost::numeric::ublas::vector MyFunc(::boost::numeric::ublas::vector in);
}
//--- file myproject_last.hpp ---
namespace MyNamespace {
using ::boost::numeric::ublas::vector;
}
//--- file myproject.cpp ---
#include "myheader.hpp"
// ...other includes
#include "myproject_last.hpp"
詳細、この回避策、およびアドバイスについては GotW#53:名前空間への移行 を参照してください:「宣言を使用する名前空間はヘッダーファイルに表示されるべきではありません。」
名前のない名前空間をusing宣言の周りに追加して(それらの名前が表示されないようにするため)、次に名前のない名前空間の外側に別の名前を付けて(目的の名前を作成するitself visible)、問題1を回避できます。 、しかしそれでも問題2に悩まされてヘッダーを醜くしている:
//--- file myheader.hpp ---
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
namespace {
using ::boost::numeric::ublas::vector;
vector MyFunc(vector in);
}
using MyFunc; // MyNamespace::(unique)::MyFunc > MyNamespace::MyFunc
}
これらの問題があるため、非ヘッダー(.cc/.cpp)ファイルでのみ使用宣言を使用する必要があります。これは他のファイルには影響しないため、問題1は回避されます。すべてのヘッダーが含まれているため、問題2は回避されます。この場合、他のファイルに影響を与えないため、名前空間に入れるかどうかは好みの問題です。 using宣言自体で常に完全修飾名を使用するのが最も安全です(絶対、::
で始まります)。
最も簡単なのは、すべてのusing宣言をインクルードの後でファイルの先頭に配置することですが、名前空間の外にあります。これは安全で、明確で、読みやすく、ファイル全体で名前を使用できます。いくつかの一般的な逸脱:
(名前付き)名前空間内の相対名を使用した宣言:error-proneこれはより簡潔であり、いくつかの明確さ(関連する名前空間で使用される関連名)を追加しますが、あいまいである可能性があり(相対パスを含むインクルードのように)、避ける方が安全です。
using ::foo::bar;
namespace foo { ... }
namespace foo {
// Implicitly ::foo:bar, could be ::bar, or ::other::foo::bar.
using bar;
}
名前付き名前空間内の絶対名を使用した宣言:pointlessこれは名前空間にのみ名前を導入しますが、.cc/.cppファイルを含めるべきではないため、気にする必要はありません。
namespace foo {
using ::bar;
}
名前のない名前空間内のusing宣言:無意味で、少し危険です。たとえば、名前のない名前空間に関数がある場合(実装の詳細など)、その戻り値の型またはparam型の使用宣言を持つことができます。これにより、その名前空間だけに名前が導入されます(他のファイルから参照できないため)。ただし、.cc/.cppファイルは含めないでください(名前のない名前空間は特に回避するためです)。リンク時に名前が衝突します。これはここでは適用されません。これは単なるコンパイル時のエイリアスです)。さらに悪いことに、その名前がすでに存在する場合はあいまいさが生じます。
他の名前空間を汚染することはありませんが、MyNamespace名前空間を汚染することは確かです。