不相关的已删除运算符更改重载解析的行为
Unrelated deleted operator changes behaviour of overload resolution
我今天遇到了一个奇怪的情况,用某些参数声明一个已删除的运算符会改变看似无关的代码的行为。
我将其简化为以下内容。从这个开始:
namespace N
{
enum E { A, B };
struct C
{
C(E);
private:
C(int);
};
}
N::E operator|(N::E, N::E);
namespace N
{
void Waldo()
{
C(A | B);
}
}
请注意,C有两个构造函数,一个公共构造函数和一个私有构造函数。此代码进行编译,表示正在选择公共重载,因此表达式A | B
的类型为E
。这反过来意味着operator|(N::E, N::E)
已经匹配(否则A
和B
将进行隐式转换为整数,A | B
的类型将为int
,并且私有构造函数将匹配
到目前为止还不错。现在我定义了一个新的枚举类型F
,以及一个已删除的涉及F:的operator|
namespace N
{
enum E { A, B };
struct C
{
C(E);
private:
C(int);
};
}
N::E operator|(N::E, N::E);
namespace N
{
enum F {};
int operator|(F, int) = delete;
void Waldo()
{
C(A | B);
}
}
现在代码没有编译,说明C(int)
是私有的。这表明现在A | B
具有类型int
,这意味着operator|(N::E, N::E)
不再匹配。
为什么添加已删除的operator|(F, int)
会阻止operator|(N::E, N::E)
匹配
首先,请注意,被声明为delete
d是不相关的,因为删除的函数仍然参与重载解析。
现在,转到过载解决方案。参见13.3.1.2/3:
三组候选函数,指定成员候选,非成员构建
(没有候选成员,因为E
不是类类型。)我们从中知道,运算符重载是由非限定查找发现的。因此,当我们查阅3.4.1[不合格查找]时,我们发现
名称查找就会结束。
由于在命名空间N
中引入了第二个运算符重载,因此会首先找到它,并停止名称查找。此时,重载集由int N::operator|(N::F, int)
和内置运算符组成。继续13.3.1.2/6:
过载解决的候选函数集是成员候选、非成员候选和内置候选的联合。
只有内建是可行的(因为您不能隐式地将E
转换为F
),因此会选择它。
问题的解决方案很简单。
将operator|
放在与类型相同的命名空间中。现在,ADL(自变量相关查找)开始了,它被发现,即使也有不相关的operator|
可见。
活生生的例子。注意,尽管在namespace Z
中使用了|
,但还是发现了N::operator|
。
重载类型的自由运算符的合适位置是该类型所在的namespace
,而不是全局命名空间。
- 如何从重载解析中删除重载函数?
- 指向重载静态成员的函数指针 - 在unique_ptr中用作自定义删除器
- C++:如果我重载新运算符,我是否也必须重载删除运算符?
- 为什么在析构函数中引发异常时不调用重载删除
- 重载运算符>>从二叉树中删除节点
- 重载运算符 new/new[] 删除/删除[] 全局C++
- C++ 运算符删除重载对派生类不起作用
- 防止将临时传递为常量引用的方法,该方法比删除 r 值重载更好地缩放
- 使用(模板化的)已删除的函数重载来防止常见的算术转换
- 全局删除的冲突重载
- 如何删除模板类的输出操作程序重载的错误
- 不能只删除方法的常量重载?
- 当调用来自同一个重载的非常量版本成员函数时,可以删除常量限定符吗
- 带有已删除的移动构造函数的不明确重载
- 运算符新建和删除重载作用域
- 在c++中的赋值运算符重载方法中删除旧的动态分配内存
- 如何摆脱此错误 main.cpp:43:19: 错误:没有可行的重载 '=' novowels[100] = 删除(名称[100]);
- C++ 中的重载删除运算符
- 析构函数与重载删除
- 操作符重载删除函数