C++ `使用命名空间指令使全球范围操作员消失了吗?

发布于 2025-01-18 07:41:57 字数 964 浏览 2 评论 0原文

由于我不明白的原因,以下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),编译器文件投诉:错误c2678:biarmin' - ':no发现操作员采用了“ std :: strong_ordering”类型的左手操作数(或没有可接受的转换)

当使用命名空间 在(1)上删除指令时,编译器愉快地找到了运算符 -在全球范围上定义的std :: stront :: strong_ordering类型。

这引起了一系列问题:

  1. 该vs 2022行为(a)一个错误,(b)允许甚至(c)根据语言标准进行强制性?
  2. 如果是(b)或(c),如何?标准中的哪些特定句子允许/要求编译器到 在全局范围上找到ocerator-
  3. 您将如何建议解决这个问题,以假设使用命名空间指令的是否可以留下来?

实时演示

For reasons I do not understand, the following C++ code fails to compile on VS 2022 (dialect set to 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) !!!
    }
}

At (2), the compiler files a complaint: error C2678: binary '-': no operator found which takes a left-hand operand of type 'std::strong_ordering' (or there is no acceptable conversion).

When the using namespace directive at (1) is removed, the compiler happily finds the operator- defined at global scope for the std::strong_ordering type.

This gives rise to a set of questions:

  1. Is this VS 2022 behavior (a) a bug, (b) allowed or even (c) mandatory according to the language standard?
  2. In case of (b) or (c), how? Which specific sentences in the standard allow/mandate the compiler to not find the operator- at global scope?
  3. How would you suggest to work around the issue, presuming that the using namespace directive is there to stay?

Live demo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

橘香 2025-01-25 07:41:57

您的编译器是正确的,我希望其他编译器同意。

有关A 使用指导的行为,请参见C ++ 20 [namespace.udir]/2:

a 使用指导性指定指定名称空间中的名称可以在使用指导后出现使用出现的范围中使用。 。在不合格的名称查找(6.5.2)中,名称出现
好像它们是在最近的封闭名称空间中声明的,该空间包含使用指导和提名的名称空间。


换句话说,如果n1包含使用名称空间N2;,则仅出于未合格的名称查找的目的,则n2中的名称将显示为它们处于n1n2的最低祖先名称空间。由于n2n1N2在执行不合格的名称查找时出现N1

这意味着运算符的无限制查找 -将找到n2 ::操作员 -,并且不会进入全局名称空间以继续搜索其他声明。请参阅[basic.lookup.unqual]/1:

在6.5.2中列出的所有情况下,范围搜索以各自类别中列出的顺序搜索声明;名称查找后立即结束该名称的声明。如果找不到声明,则该程序的形式不佳。

要解决这个问题,有两种策略。一种是将运算符 -放置在声明该类型的名称空间中的类型,以便通过参数依赖性查找找到。但是,您不允许将操作员的过载添加到std名称空间。另一种策略是重新运行操作员 -您要使用使用declaration

using namespace N2;
using ::operator-;

这有效地带来了您想要的operator-“一个级别”更深的”,将其与其他运算符 -相同的水平,这要归功于使用指导性的,因此,无限制的名称查找将同时找到,并且编译器将执行执行超负荷分辨率。

Your compiler is correct, and I would expect other compilers to agree.

For the behaviour of a using-directive, see 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.

In other words, if N1 contains using namespace N2;, then only for the purposes of unqualified name lookup, names in N2 will appear as if they are in the lowest common ancestor namespace of N1 and N2. Since N2 is inside N1, the lowest common ancestor namespace is just N1, and that means the operator- in N2 appears inside N1 when unqualified name lookup is performed.

This means that unqualified lookup for operator- will find N2::operator-, and it won't proceed to the global namespace to continue searching for additional declarations. See [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.

To work around the issue, there are two strategies. One is to place the operator- for your type in the namespace where that type is declared, so it can be found via argument-dependent lookup. However, you are not allowed to add operator overloads to the std namespace. The other strategy is to redeclare the operator- you want using a using-declaration:

using namespace N2;
using ::operator-;

This effectively brings the operator- that you want "one level deeper", putting it at the same level as the other operator- that appears thanks to the using-directive, so unqualified name lookup will find both, and the compiler will perform overload resolution.

方圜几里 2025-01-25 07:41:57

运算符 - 来解决问题。

...
namespace std
{
    strong_ordering operator-(strong_ordering o);
}
...

我现在通过让运算符 -std ::,即:我不确定搞乱是否很好的练习中,通过使用std :: namespace;欢迎评论。

I'm now working around the issue by having the operator- live in std::, i.e.:

...
namespace std
{
    strong_ordering operator-(strong_ordering o);
}
...

However, I'm not really sure whether it is good practice to mess with the std:: namespace; comments welcome.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文