【问题标题】:why C++ operator overloading requires "having at least one parameter of class type"?为什么 C++ 运算符重载需要“具有至少一个类类型参数”?
【发布时间】:2014-06-23 16:47:45
【问题描述】:

《C++入门第5版》第14.1章内容如下,

操作符函数必须要么是类的成员,要么至少有一个类类型的参数。

例如,string("hello")+"world" 编译 "hello"+"world" 不会。当我想在两个 C 字符串上重载 + 时。

std::string operator+ (const char* s1, const char* s2)

我收到以下错误。

错误:'std::string operator+(const char*, const char*)' 必须有一个类或枚举类型的参数

我有两个问题。

  1. 这个限制是语言规范的一部分吗?如果是,为什么 C++ 设计者要这样做?

  2. std::string 具有类似string (const char* s); 的构造函数,这意味着编译器可以进行从char*string 的隐式类类型转换。当我们调用"hello"+"world"时,为什么编译器不把两个char* "s 转换成两个字符串呢? 毕竟两个std::strings 上有一个重载的"+"。 string operator+ (const string& lhs, const string& rhs);

【问题讨论】:

  • 如果你可以重载两个指针的加法,这将创建一些“有趣”的指针算法。
  • @Anycorn Brilliant。在我定义了 string_add (std::string s1, std::string s2) 之后,我可以毫无问题地调用 "string_add("hello", "world")"。因此,只要函数不是运算符重载,似乎就有隐式转换。
  • @PengZhang 那是因为 std::string 可以从 char* 构造。顺序是,从 char* 构造 s1,从 char* 构造 s2,然后将这些临时变量传递给您的函数。
  • @W.B.有运算符重载“字符串运算符+(const string& lhs, const string& rhs);”为什么编译器不将两个 C 字符串转换为 std::strings 并匹配“运算符 +”?函数匹配和类型转换,先执行哪一个?
  • 我相信如果一种类型不是内置的就足够了,例如一个枚举。

标签: c++ c++11 operator-overloading


【解决方案1】:
  1. 此限制是语言规范的一部分吗?

是的,是的。为什么?嗯,主要原因可能是因为重新定义标准类型的常用运算符被视为混淆。想象一下重载operator+(int,int)operator+(int,char*)。这将改变现有代码的含义!

您可能会争辩说,标准类型之间存在未定义的运算符,因此您可以安全地覆盖它们,例如operator*(char*,int),但这通常被视为无用。

此外,运算符重载必须是全局的,(或者在其某些成员的命名空间中(参数依赖查找),但对于标准类型,没有命名空间可以依赖),因此想要覆盖它们的库之间的互操作性将是一场噩梦。

  1. "hello" + "world": 为什么编译器不把两个 char* " 转换成两个字符串?

好吧,一方面,std::operator+(const std::string&, const std::string&)namespace std; 中,所以默认情况下不会找到它。

您可以尝试显式调用运算符:std::operator+("hello", "world"),但是很遗憾,operator+() 重载太多,其中很多是模板,因此调用不明确。

因此,考虑到所有这些,我认为一个合理的要求是至少有一个运算符是用户定义的类型。想一想:全局和命名空间问题解决了,可以使用ADL(其实就是为了这个用途而发明的)而且不可能重新定义一个已有含义的运算符(除了operator,()operator&(),我认为,但是谁想覆盖这些...)。

【讨论】:

  • operator= 用于类类型具有现有含义。
  • @aschepler:哦,你是对的!虽然在这种情况下,重载并不深奥,但需要保证类不变量。
  • “这通常被视为相当无用”阅读此问题:stackoverflow.com/q/5703271/126995
【解决方案2】:

这个限制是语言规范的一部分吗?如果是,为什么 C++ 设计者要这样做?

是的,是的。引用N3337, §13.5.6 [over.oper]:

操作符函数要么是非静态成员函数,要么是非成员函数,并且至少有一个类型为类、类引用、枚举或枚举引用的参数。

关于“为什么”部分,因为删除此要求意味着您可以重载已定义运算符语义的内置类型的运算符。这是完全不可取的事情。

例如,您认为定义这个有意义吗?

int operator+(int a, int b) { return a - b; }

它是否允许更多人在阅读您的代码时对您的程序进行推理,还是只是一些令人惊讶的事情有效地破坏了您的推理?

如果我们在游戏中获得指针会发生什么?

bool operator==(const char *str1, const char *str2) { 
    return strcmp(str1, str2) == 0;
}

您是否希望 operator== 现在取消引用内存?我不会。令人惊讶。这违背了标准已经对这些运营商所说的内容。它打破了过去 25 年中每个 C 程序的行为方式。语言不应该让你做这种滥用。

【讨论】:

    【解决方案3】:

    正如 Bjarne 在设计与进化中所说,我们的目标是让语言扩展变得容易,而不是变异它。如果您只允许重载内置类型,您将更改语言,并且允许这样做违背了设计目标。 (例如,它会鼓励形成互不相容的方言并破坏社区。)

    【讨论】:

    • AFAICT,这是避免允许像T* operator+(T*, ptrdiff_t) 及其补充T* operator+(ptrdiff_t, T*) 这样的已定义语义重载的一个很好的理由,但我怀疑它是否是禁止重载 previously undefined 运算符(如 string operator+(const char*, const char*))的充分理由
    • @Massa:嗯,之前有一条规则说,如果ab 都是指针,那么表达式a + b 是错误的,所以你会变异规则。
    • @Massa:另外,这可能是 C++ 设计如何解决您的问题的一个很好的例子:您可以编写 "abc"s + "def"s 来获取字符串。明智地添加新功能(用户定义的文字)不是改变现有语义,而是提供了一个简洁的解决方案。
    • @KerrekSB,我还在等"abc"sv,我想还有一个string_view 课程:p
    • @Massa:这些都还没有最终确定,因此当前实现中的不一致和错误并不奇怪。给它一些时间(或者在 Clang 错误跟踪器上提交错误?)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 2018-09-12
    • 2018-02-22
    相关资源
    最近更新 更多