operator-overloading 相关问题

运算符重载是编程语言的一个特性,它允许运算符的自定义实现,具体取决于所涉及的操作数的类型。有些语言允许定义新的运算符,而其他语言只允许重新定义现有的运算符。

为什么这个运算符重载可以在 MSVC 和 GCC 中编译,但不能在 Clang 中编译?

我正在尝试创建一个strong_alias包装类型,它可以与原始类型互换,但不能与其他strong_alias互换。 #包括 #包括 模板 我正在尝试创建一个 strong_alias 包装类型,它可以与原始类型互换,但不能与其他 strong_aliases 互换。 #include <array> #include <stdint.h> template <typename T, typename tag> requires std::is_arithmetic<T>::value class strong_alias { public: using type = T; constexpr strong_alias() : _value{} { } template <typename other> requires std::is_arithmetic<other>::value constexpr strong_alias(const other &set_value) : _value{T(set_value)} { } constexpr T &value() { return _value; } constexpr const T &value() const { return _value; } template <typename other> requires std::is_arithmetic<other>::value constexpr operator other() const { return other(value()); } constexpr bool operator==(const strong_alias &rhs) const { return value() == rhs.value(); } constexpr strong_alias operator*(const strong_alias &rhs) const { return value() * rhs.value(); } private: T _value; }; using A = strong_alias<int8_t, struct A_tag>; static_assert(std::is_assignable<A, A>()); static_assert(std::is_assignable<A, int>()); static_assert(std::is_assignable<int &, A>()); using B = strong_alias<int8_t, struct B_tag>; static_assert(not std::is_assignable<A, B>()); static_assert(not std::is_assignable<B, A>()); static_assert(A(-3) == A(3) * -1); // builds with MSVC and GCC but not Clang int main() { } 上面的代码可以使用 MSVC (/std:c++latest) 和 GCC (-std=c++2c) 进行编译,但不能使用 Clang (-std=c++2c) 进行编译。 这里有编译器资源管理器演示。 哪些编译器能够正确处理这段代码?在 Clang 下使其工作的首选方法是什么? Compiler Explorer 的 Clang 输出: <source>:61:29: error: use of overloaded operator '*' is ambiguous (with operand types 'A' (aka 'strong_alias<signed char, A_tag>') and 'int') 61 | static_assert(A(-3) == A(3) * -1); // builds with MSVC and GCC but not Clang | ~~~~ ^ ~~ <source>:43:25: note: candidate function 43 | constexpr strong_alias operator*(const strong_alias &rhs) const | ^ <source>:61:29: note: built-in candidate operator*(float, int) 61 | static_assert(A(-3) == A(3) * -1); // builds with MSVC and GCC but not Clang | ^ <source>:61:29: note: built-in candidate operator*(double, int) <source>:61:29: note: built-in candidate operator*(long double, int) <source>:61:29: note: built-in candidate operator*(int, int) [...many lines omitted...] <source>:61:29: note: built-in candidate operator*(unsigned long long, unsigned long) <source>:61:29: note: built-in candidate operator*(unsigned long long, unsigned long long) <source>:61:29: note: built-in candidate operator*(unsigned long long, unsigned __int128) 1 error generated. Compiler returned: 1 首先,我们应该在重现问题的同时尽可能地简化代码。令人失望的是,OP 似乎没有做出任何尝试这样做。这是最小化版本: #include <type_traits> struct A { A(int set_value) : _value{set_value} { } template <typename other> requires std::is_arithmetic<other>::value operator other() const { return other(_value); } A operator*(const A &rhs) const { return _value * rhs._value; } int _value = 0; }; int main() { (void)(A(3) * -1); } 现在,这个*可以有几种不同的解释方式。它可以是 A::operator*,可通过第二个操作数从 int 到 A 的隐式转换来调用,也可以是内置算术 operator*,可通过第一个操作数的隐式转换来调用操作数从 A 到算术类型(可能第二个操作数也经历到不同算术类型的隐式转换)。 Clang 可以帮助打印出所有候选人。 之所以不明确,是因为当调用operator*时,我们得到了与第一个参数类型的精确匹配以及与第二个参数类型的用户定义转换。当调用内置的 operator*(int, int) 时,我们得到一个用户定义的转换,其第一个参数类型与第二个参数类型完全匹配。所以这两个候选人都不比另一个更好。 叮当是正确的。至于如何编译原始代码,由于问题是由两个候选代码引起的,每个候选代码都可以通过不同的隐式转换实现,因此您的选择基本上是: 提供比所有当前现有候选更好的您自己的重载,例如,一个 operator*,其第一个参数类型为 const strong_alias&,第二个参数类型为算术类型,或者 将 A 的转换构造函数更改为显式,或者 将转换函数 (A::operator other) 更改为显式,或者 以上任意组合 我无法告诉您哪个选项最适合您。这取决于您希望您的类型提供的 API。 GCC 和 MSVC 之所以不认为它含糊不清,可能与他们使用不同的算法来确定哪些内置运算符是候选者有关。在这种特殊情况下,他们只是给出了错误的答案,但更普遍的是,该标准没有就如何在其他可能涉及无限的初始候选者的情况下缩小一组相关内置候选者的范围提供足够的指导。请参阅CWG2844。

回答 1 投票 0

重载的“operator+”必须是一元或二元运算符错误

按照此答案中给出的建议,我在简单的 Point 类中重载了 + 运算符,如下所示( += 重载工作正常)。 点运算符+ (Point p1, const Point& p2) { ...

回答 4 投票 0

这里是新手。请帮助我,因为它会引发错误

我的 C++ 代码中遇到错误,我正在寻求解决该错误的帮助。当尝试使用 << operator with std::cout to output the result of a function call, I receive the

回答 1 投票 0

unordered_map 中用户定义类型的运算符重载()

我正在看这篇文章 C++ unordered_map 使用自定义类类型作为键 我知道我们需要为自定义类型键重新定义相等性和哈希码。 我知道运营商如何过度...

回答 2 投票 0

何时重载逗号运算符?

我经常看到关于在 C++ 中重载逗号运算符的问题(主要与重载本身无关,但类似于序列点的概念),这让我想知道:...

回答 11 投票 0

std::ostream 运算符的 C++ 问题<< in template class

如果我将类代码放在同一个头文件或同一个 .cpp 文件中,我就不会遇到这个问题,但是当我将类规范放在头文件中并将类代码放在单独的 .cpp 文件中时,我得到了这个

回答 1 投票 0

C++:当运算符=重载时出现 EXC_BAD_ACCESS

之前问过operator=重载错误,问题因重复而被关闭,试图修复代码,却出现了新的错误。 const 限定后旧错误消失了, 但是一个新的

回答 1 投票 0

如何重载基本类型(int、real...)的相等运算符?

是否可以重载 PostgreSQL 中现有的相等运算符,以给出两个 int 或 real 类型值之间的相等性的新定义?我认为这违反了超载规则...

回答 2 投票 0

如何在作用域枚举上重载 |= 运算符?

如何在强类型(作用域)枚举(在 C++11、GCC 中)上重载 |= 运算符? 我想测试、设置和清除强类型枚举上的位。为什么强类型?因为我的书说它很好

回答 7 投票 0

如何在 C++ 中重载枚举的 ++ 运算符

这是我尝试过的,但我发现如果我将变量分配给另一个变量,重载只会增加变量。即,我对其进行增量的变量的值不会增加。所以,...

回答 3 投票 0

编写运算符重载时删除样板代码

当我制作 vector3 结构时,我注意到运算符功能几乎彼此相同 矢量3矢量3::运算符+(浮点标量)const { 返回向量 3(x + 标量, y + 标量, z + sc...

回答 1 投票 0

如何区分(重载时)operator++ 的前缀和后缀形式? (C++)

因为我已经为迭代器类重载了operator++ 模板 类型名列表::iterator& list::iterator::operator++() { //东西 } 但当我尝试...

回答 4 投票 0

Python 类定义中 __add__() 和 add() 的区别

我正在用Python学习OOP,我遇到了想要添加两个对象的情况。我见过有人使用以下格式: GFG 类: def __init__(self, val): self.val =...

回答 1 投票 0

函数指针的操作

我正在努力弄清楚函数指针和 lambda 函数的语法,我需要一些帮助。 我想做的是创建一个具有参数函数指针并能够添加的类,

回答 1 投票 0

如何在 C++ 标准库类中重载已重载的运算符?

我正在尝试实现我自己的矩阵类(二维数组),它使用 [] 运算符来访问其内容并使用 std::unique_ptr<> 来保存它 - 我知道有更简单/更好的方法解决...

回答 1 投票 0

C++ 赋值运算符重载意外行为

我是 C++ 新手,正在尝试以正确的方式学习它。特别是,我正在下面的类中练习一个简单的赋值运算符重载 类点{ 民众: 整数x; 整数y; ...

回答 1 投票 0

如何在C++中重载一元减运算符? [重复]

我正在实现向量类,我需要获得某个向量的反面。是否可以使用运算符重载来定义此方法? 这就是我的意思: Vector2f 向量1 = -向量2; 这里是...

回答 2 投票 0

基类中的多维下标运算符不参与推导此的重载解析

示例在这里(godbolt) 我的基类下标中有一个多维下标运算符的默认实现。创建派生 t0 以提供下标运算符。 结构体下标...

回答 1 投票 0

重载 C++ 运算符时出现问题 - 创建 char 缓冲区但其大小太大

我正在通过 Udemy 课程学习 C++,并且遇到了重载 * 运算符的奇怪问题。 在Main中,代码是: S1=S2*3; //S1 是一个具有指向 char 数组的单个指针的类。

回答 1 投票 0

C++ 重载运算符问题创建 Char Buff 且实际大小太大

我正在通过 Udemy 课程学习 C++,并且遇到了重载 * 运算符的奇怪问题。 Main中,代码为S1 = S2 * 3; //S1 是一个具有指向 char 数组的单个指针的类。

回答 1 投票 0

© www.soinside.com 2019 - 2024. All rights reserved.