usingディレクティブを用いたコードで、次のようなエラーが発生してしまうことがある。
using namespace std;
struct string {};
string s; // error: reference to 'string' is ambiguous
template<class T> T max(T a, T b) { return a > b ? a : b; }
int v = max(1, 2); // error: call to 'max' is ambiguous
原因は、独自に宣言したstring
クラスやmax
関数が名前空間std
内で既に宣言されているためである。スコープ解決演算子(::
)を用いてグローバルスコープ側の機能名と名前空間内の機能名を区別する必要がある。
using namespace std;
struct string {};
::string s; // OK: グローバルスコープ側のstringクラス
std::string t; // OK: 名前空間std側のstringクラス
template<class T> T max(T a, T b) { return a > b ? a : b; }
int v = ::max(1, 2); // OK
int w = std::max(1, 2); // OK
改修箇所が多くなる場合は、using宣言を用いて一括で名前解決するのも良いだろう。
using namespace std;
struct string {};
namespace ns {
using ::string;
void g() { string s; } // グローバルスコープ側のstringクラス
void f() { string s; } // グローバルスコープ側のstringクラス
void h() {
using std::string;
string s; // 名前空間std側のstringクラス
}
}
struct T {
// クラススコープではusing宣言が行えない
using std::string; // error: using declaration in class refers into 'std::', which is not a class
using string = std::string; // OK(エイリアス宣言で対処)
};
同等のエラーは関数やテンプレートクラス、クラスオブジェクトとの競合によっても発生する。
using namespace std;
int plus(int a, int b) { return a + b; }
int main() {
int v = plus(1, 2); // error: reference to 'plus' is ambiguous
int v = ::plus(1, 2); // OK(名前空間を明示する)
using ::plus;
int w = plus(1, 2); // OK(using宣言を行う)
}
関数同士の競合が発生した場合はcall to 'x' is ambiguous
という異なるエラーメッセージになるが、エラーの趣旨や対処方法は変わらない。
using namespace std;
template<class T> T max(T a, T b) { return a > b ? a : b; }
int v = max(1, 2); // error: Call to 'max' is ambiguous
int v = ::max(1, 2); // OK
ちなみに、インライン名前空間を用いた場合も、今回と同様の問題が発生する場合があるため注意したい。
namespace A {
inline namespace B {
struct X {};
}
struct X : B::X {};
}
A::B::X x; // OK
A::X y; // error: reference to 'X' is ambiguous
なお、もっとも賢明な対策はusing namespace std;
によるusingディレクティブを用いないようにすることである。
using namespace std; の危険性と注意点・代替案