《C++覆辙录》——2.11:运算符函数名字查找的反常行为

2.11:运算符函数名字查找的反常行为

重载的运算符真的只不过就是可以用中序语法调用的地地道道的成员函数或非成员函数罢了。它们是“语法糖”:

class String{
public:
  String &operator =(const String&);
  friend String operator +(const String&, const String&);
  String operator–();
  operator const char*() const;
  // ...
};
String a, b, c;
// ...
 a = b;
a.operator =(b); // 和上一个语句意义相同
a + b;
operator + (a, b); // 和上一个语句意义相同
a = -b;
a.operator =(b.operator-()); // 和上一个语句意义相同
const char *cp = a;
cp = a.operator const char*(); // 和上一个语句意义相同```
如果要评选“最佳清晰奖”,那么中序记法必可荣膺。典型情况下,我们要使用一个被重载的运算符时都是用中序记法的(即“左手边操作数运算符右手边操作数”的写法)。毕竟我们之所以要重载运算符最原始的出发点不就是这个么?

一般地,当我们不用中序记法时,函数调用语法比对应的中序记法更清晰。一个教科书般的例子就是基类的复制赋值运算符在派生类的复制赋值运算符实现中被调用的场合:

class A : {
protected:
  A &operator =(const A &);
  //…
};
class B : public A {
public:
  B &operator =(const B&);
  //…
};
B &B::operator =(const B&b){
  if (&b != this){
    A::operator =(b); // 好过"(static_castconst>(this))=b"
               // 为B的其他局部变量赋值
  }
  return *this; ⑾
}`
⑾译者注:返回*this是一个习惯用法,支持连续赋值。
还有一些场合我们使用函数调用语法而不用中序记法——尽管中序记法在这些场合的使用完全正确合理——中序记法在这些场合显得太怪异丑陋,会让一个维护工程师花几分钟才能回过神来:

value_type *Iter::operator ->() const
{return &operator*();} // 好过"&*(*this)"```
还有一些让人左右为难的情况,不管中不中序,写出来的东西都挺难看的:

bool operator !=(const Iter &that) const
  {return !(*this == that);} // 或者"!operator==(that)"`
无论如何请注意,使用中序语法时的名字查找序列和使用函数调用语法时不同,这会带来出人意料的结果:

class X{
public:
  X &operator %( const X&) const;
  void f();
  // ...
};
X &operator %(const X&, int);
  void X::f(){
  X& anX = *this;
  anX % 12; // 没问题,调用非成员函数
  operator %(anX, 12); // 错误!
}```
当我们使用函数调用语法时,名字查找序列遵从标准形式。在成员函数X::f的情况下,编译器首先在class X里找一个名字叫“`operator %`”的函数。只要找到了,它就不会在更外层的辖域里继续找其他同名的函数了。

不幸的是,我们企图向一个二元运算符传递3个实参。因为成员函数`operator %`有一个隐式的实参`this`,我们显式向它传递的2个实参会让编译器误以为我们想要把一个二元运算符以不正确的三元形式调用。一个正确的调用或者显式地识别出非成员版本的`operator %`(`::operator %(anX, 12)`),或者向成员函数`operator %`传递正确数量的实参(`operator %(anX)`)。
时间: 2024-07-31 09:15:12

《C++覆辙录》——2.11:运算符函数名字查找的反常行为的相关文章

《C++覆辙录》——导读

前言 C++覆辙录 本书之渊薮乃是近20年的小小挫折.大错特错.不眠之夜和在键盘的敲击中不觉而过的无数周末.里面收集了普遍的.严重的或有意思的C++常见错误,共计九十有九.其中的大多数,(实在惭愧地说)都是我个人曾经犯过的. 术语"gotcha"1有其云谲波诡的形成历史和汗牛充栋的不同定义.但在本书中,我们将它定义为C++范畴里既普遍存在又能加以防范的编码和设计问题.这些常见错误涵盖了从无关大局的语法困扰,到基础层面上的设计瑕疵,再到源自内心的离经叛道等诸方面. 大约10年前,我开始在

《C++覆辙录》——常见错误1:过分积极的注释

第1章 基础问题 C++覆辙录 说一个问题是基础的,并不就是说它不是严重的或不是普遍存在的.事实上,本章所讨论的基础问题的共同特点比起在以后章节讨论的技术复杂度而言,可能更侧重于使人警醒.这里讨论的问题,由于它们的基础性,在某种程度上可以说它们普遍存在于几乎所有的C++代码中. 常见错误1:过分积极的注释 很多注释都是画蛇添足,它们只会让源代码更难读,更难维护,并经常把维护工程师引入歧途.考虑下面的简单语句: a = b; // 将b赋值给a 这个注释难道比代码本身更能说明这个语句的意义吗?因而

《Python参考手册(第4版•修订版)》——1.11 函数

1.11 函数 使用def语句可以创建函数,如下例所示: def remainder(a,b): q = a // b # //是截断除法运算符 r = a – q*b return r 要调用函数,只要使用函数名加上用圆括号括起来的参数即可,如result = remainder(37, 15).如果要让函数返回多个值,可以使用元组,如下所示: def divide(a,b): q = a // b # 如果a和b是整数,q就是整数 r = a – q*b return (q,r) 使用元组返

嵌入式-c中的宏定义可以是2个函数吗,我把别人的函数移到自己文件中,但是有些同样功能的函数名字不一样

问题描述 c中的宏定义可以是2个函数吗,我把别人的函数移到自己文件中,但是有些同样功能的函数名字不一样 还要后面的括号以及内部的形参吗? 解决方案 你的提问: delay_us()是我从别的文件中copy过来的,只有函数名,并没有这个函数的定义,如果我用#define f1() f2()的形式,应该是替换了整个函数吧,程序中没有定义能行吗?我的意思就是吧f1替换成f2,那么#define f1 f2,这样对f1(),f2()函数会起作用吗. 我的理解: 代码中 没有f1 函数的实现 ,但是有 函

c++11-关于C++11 bind函数的疑问

问题描述 关于C++11 bind函数的疑问 deque coll2 = {1,2,3,4,5,6,7,8,9}; coll2.erase( remove_if(coll2.begin(), coll2.end(), bind(logical_and(), bind(greater_equal(), _1, 5), bind(less_equal(), _1, 8))), coll2.end()); 上面表达式的意思是删除coll2里大于等于5小于等于8的元素, 外层的erase和remove_

《JavaScript应用程序设计》一一2.11 多态函数

2.11 多态函数 在计算机学科中,多态性意味一件事物的行为取决于它当前所处的上下文环境,就像单词一样,在不同的句子中的含义也不尽相同,如下例中"东西"一词. · 迈尔斯是韩国东西大学专门研究北韩官方宣传与传播的教授. · 据谣传这所房间每天夜里都有什么东西吼叫. · 这是个交通枢纽,铁路由此向东西南北伸展出去. 同理,多态函数意味着函数在执行期间的行为由传入的具体参数决定,在JavaScript中,这些参数存储在一个被称为arguments的类数组对象中,说它是类数组是因为它本身不具

《C和C++代码精粹》——1.11 函数重载和函数模板

1.11 函数重载和函数模板 C和C++代码精粹程序清单1.4中的交换函数(swap)只有在交换整数时才有用.如果要交换两个任何系统预定义的数据类型中的对象该么办呢?C++允许定义多个同名函数,只要它们的特征不同.因此就可以为所有系统预定义的数据类型定义一个交换函数: void swap(char &,char &); void swap(int &,int &); void swap(long &,long &); void swap(float &

PHP 11:函数

原文:PHP 11:函数本文章介绍PHP的函数.如何学习呢?可以从以下几个方面考虑  函数是如何定义的?区分大小写吗? 函数的参数是如何定义的? 函数是否支持重载? 函数的返回值是如何定义的. 函数有变量函数吗? 如果把上面的问题搞清楚了,相信函数你也就掌握了.还是一个个看吧.  函数是如何定义的?区分大小写吗?         首先函数对大小写不敏感.但是还是建议你采用和函数声明时的一样.        函数是如何定义的呢?语法可以为: <?phpfunction func($arg_1, $

《C++覆辙录》——第2章 语法问题2.1:数组定义和值初始化的语法形式混淆

第2章 语法问题 C++覆辙录C++语言的语法和词法结构博大精深.此复杂性的一部分是从C语言那里继承而来的,另一部分则是为支撑某些特定的语言特性所要求的. 本章中我们将考察一组语法相关的头疼问题.其中有些属于常见的手误,但是错误的代码仍然能够通过编译,只不过会以出人意料的方式运行罢了.另外一些则是由于一段代码的语法结构及它们的运行期行为不再互为表里.其余的部分,我们主要研究语法层面的灵活余地带来的问题:明明是一字不差的代码,不同的软件工程师能从中得出大相径庭的结论来. 2.1:数组定义和值初始化