C++ `using namespace` 指令使全局范围运算符消失?
C++ `using namespace` directive makes global-scope operator disappear?
由于我不明白的原因,以下 C++ 代码无法在 VS 2022(方言设置为 C++20)上编译:
#include <compare>
namespace N1
{}
namespace N1::N2
{
class A {};
A operator-(A&);
}
std::strong_ordering operator-(std::strong_ordering o);
namespace N1
{
using namespace N2; // (1) !!!
std::strong_ordering foo();
inline std::strong_ordering bar()
{
return -foo(); // (2) !!!
}
}
在(2)处,编译器投诉:error C2678: binary '-': no operator found which takes a left-hand operand of type 'std::strong_ordering' (or there is no acceptable conversion)
.
当 (1) 处的 using namespace
指令被移除时,编译器会很高兴地找到在全局范围内为 std::strong_ordering
类型定义的 operator-
。
这引发了一系列问题:
- 根据语言标准,此 VS 2022 行为是否 (a) 错误,(b) 允许甚至 (c) 强制?
- 如果是 (b) 或 (c),怎么办? allow/mandate编译器在全局范围内不找到
operator-
的具体语句有哪些?
- 假设
using namespace
指令一直存在,您建议如何解决这个问题?
我现在正在通过让 operator-
住在 std::
来解决这个问题,即:
...
namespace std
{
strong_ordering operator-(strong_ordering o);
}
...
但是,我不太确定乱用 std::
命名空间是否是个好习惯;欢迎评论。
您的编译器是正确的,我希望其他编译器也同意。
对于 using-directive 的行为,参见 C++20 [namespace.udir]/2:
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (6.5.2), the names appear
as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
换句话说,如果 N1
包含 using namespace N2;
,那么仅出于非限定名称查找的目的,N2
中的名称将显示为就好像它们在最低共同祖先中一样N1
和 N2
的命名空间。由于 N2
在 N1
内,最低的公共祖先命名空间只是 N1
,这意味着 N2
中的 operator-
出现在 N1
内时执行非限定名称查找。
这意味着 operator-
的非限定查找将找到 N2::operator-
,并且不会继续到全局命名空间继续搜索其他声明。参见 [basic.lookup.unqual]/1:
In all the cases listed in 6.5.2, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
要解决此问题,有两种策略。一种是将你的类型的 operator-
放在声明该类型的命名空间中,这样就可以通过 argument-dependent 查找找到它。但是,不允许将运算符重载添加到 std
命名空间。另一种策略是使用 using-declaration:
重新声明您想要的 operator-
using namespace N2;
using ::operator-;
这有效地带来了您想要的 operator-
“更深一层”,将其置于与另一个 operator-
相同的水平,这要归功于 using-directive,因此非限定名称查找将同时找到两者,编译器将执行重载解析。
由于我不明白的原因,以下 C++ 代码无法在 VS 2022(方言设置为 C++20)上编译:
#include <compare>
namespace N1
{}
namespace N1::N2
{
class A {};
A operator-(A&);
}
std::strong_ordering operator-(std::strong_ordering o);
namespace N1
{
using namespace N2; // (1) !!!
std::strong_ordering foo();
inline std::strong_ordering bar()
{
return -foo(); // (2) !!!
}
}
在(2)处,编译器投诉:error C2678: binary '-': no operator found which takes a left-hand operand of type 'std::strong_ordering' (or there is no acceptable conversion)
.
当 (1) 处的 using namespace
指令被移除时,编译器会很高兴地找到在全局范围内为 std::strong_ordering
类型定义的 operator-
。
这引发了一系列问题:
- 根据语言标准,此 VS 2022 行为是否 (a) 错误,(b) 允许甚至 (c) 强制?
- 如果是 (b) 或 (c),怎么办? allow/mandate编译器在全局范围内不找到
operator-
的具体语句有哪些? - 假设
using namespace
指令一直存在,您建议如何解决这个问题?
我现在正在通过让 operator-
住在 std::
来解决这个问题,即:
...
namespace std
{
strong_ordering operator-(strong_ordering o);
}
...
但是,我不太确定乱用 std::
命名空间是否是个好习惯;欢迎评论。
您的编译器是正确的,我希望其他编译器也同意。
对于 using-directive 的行为,参见 C++20 [namespace.udir]/2:
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (6.5.2), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
换句话说,如果 N1
包含 using namespace N2;
,那么仅出于非限定名称查找的目的,N2
中的名称将显示为就好像它们在最低共同祖先中一样N1
和 N2
的命名空间。由于 N2
在 N1
内,最低的公共祖先命名空间只是 N1
,这意味着 N2
中的 operator-
出现在 N1
内时执行非限定名称查找。
这意味着 operator-
的非限定查找将找到 N2::operator-
,并且不会继续到全局命名空间继续搜索其他声明。参见 [basic.lookup.unqual]/1:
In all the cases listed in 6.5.2, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
要解决此问题,有两种策略。一种是将你的类型的 operator-
放在声明该类型的命名空间中,这样就可以通过 argument-dependent 查找找到它。但是,不允许将运算符重载添加到 std
命名空间。另一种策略是使用 using-declaration:
operator-
using namespace N2;
using ::operator-;
这有效地带来了您想要的 operator-
“更深一层”,将其置于与另一个 operator-
相同的水平,这要归功于 using-directive,因此非限定名称查找将同时找到两者,编译器将执行重载解析。