zjy*_*qs 5 c++ operator-overloading language-lawyer implicit-conversion
template<typename Integral>
struct IntegralWrapper {
Integral _value;
IntegralWrapper() = default;
IntegralWrapper(Integral value)
: _value(value) {}
operator Integral() const {
return _value;
}
operator bool() const = delete;
};
int main() {
IntegralWrapper<int> i1, i2;
i1 * i2;
}
Run Code Online (Sandbox Code Playgroud)
gcc 编译成功,但 MSVC 和 clang 编译失败,错误overloaded operator '*' is ambiguous。问题来自于显式删除operator bool。
https://godbolt.org/z/nh6M11d98
哪一方(gcc 或 clang/MSVC)是正确的?为什么?
首先:删除函数并不会阻止在重载决策中考虑它(有一些与此处无关的小例外)。唯一的影响= delete是,如果通过重载决策选择转换函数,则程序将格式错误。
对于重载解析:
*对于所有升级的算术类型对,都有候选的内置运算符重载。
所以,除了使用*我们还可以考虑
auto mul(int a, int b) { return a*b; } // (1)
auto mul(long a, long b) { return a*b; } // (2)
// further overloads, also with non-matching parameter types
mul(i1, i2);
Run Code Online (Sandbox Code Playgroud)
值得注意的是,没有重载,包括bool, 因为bool被提升为int。
对于(1)两个参数所选择的转换函数,都是operator int() const从 from 实例化的,因为从tooperator Integral() const的转换比to更好。(或者至少这似乎是意图,请参阅https://github.com/cplusplus/draft/issues/2288和在重载解析中,选择使用不明确转换序列的函数是否必然导致调用格式不正确?)。intintboolint
然而(2),从int或bool到的转换都不long比另一个更好。因此,出于重载解析的目的,隐式转换序列将是不明确的转换序列。该转换序列被认为与所有其他用户定义的转换序列不同。
然后当比较哪个重载更好时,不能认为两者都比另一个更好,因为两者都对两个参数都使用用户定义的转换序列,但所使用的转换序列不具有可比性。
因此,重载解析应该会失败。如果我完成了上面开始的内置运算符重载列表,则什么都不会改变。同样的逻辑适用于所有这些。
所以 MSVC 和 Clang 的拒绝是正确的,GCC 的接受是错误的。有趣的是,我上面给出的函数的明确示例 GCC 确实按预期拒绝了。
要禁止隐式转换,bool您可以使用约束转换函数模板,该模板不允许在用户定义的转换之后使用另一个标准转换序列:
template<std::same_as<int> T>
operator T() const { return _value; }
Run Code Online (Sandbox Code Playgroud)
这将只允许转换为int. 如果您无法使用 C++20,则需要通过 .SFINAE 替换该概念std::enable_if。
| 归档时间: |
|
| 查看次数: |
92 次 |
| 最近记录: |