“C++ Primer”


可以重载的操作符

1
2
3
4
5
6
7
+` `-` `*` `/` `%` `^
&` `|` `~` `!` `,` `=
<` `>` `<=` `>=` `++` `--
<<` `>>` `==` `!=` `&&` `||
+=` `-=` `/=` `%=` `^=` `&=
|=` `*=` `<<=` `>>=` `[]` `()
->` `->*` `new` `new[]` `delete` `delete[]

不能重载的操作符

1
::` `.*` `.` `?:

选择作为成员或者非成员实现

递增和递减操作符

前置版本:前置操作符应该返回自增后或者自减后的对象的引用

1
2
3
4
5
class StrBlobPtr {
public:
  StrBlobPtr operator++(int);
  StrBlobPtr operator--(int);
};

后置版本:额外的int参数用于区分前置后置版本,后置操作符应该返回旧的(未自增或者未自减)的值。这个值将作为值返回而不是引用

1
2
3
4
5
class StrBlobPtr {
public:
  StrBlobPtr operator++(int);
  StrBlobPtr operator--(int);
};

函数调用运算符

函数调用操作符必须是成员函数。一个类型可以定义多个调用操作符版本,其中每一个必须在参数的个数或类型不一样。

1
2
3
4
5
struct absInt {
  int operator()(int val) const {
    return val < 0 ? -val : val;
  }
};
lambda 是函数对象

当我们写ambda时,编译器将其翻译成一个匿名类的匿名对象(unnamed object of an unnamed class)。这个类从ambda中产生并包含一个函数调用操作符。如:

1
2
3
4
5
stable_sort(words.begin(), words.end(),
  [](const string &a, const string &b) {
    return a.size() < b.size();
  }
);

将被翻译成

1
2
3
4
5
class ShorterString {
public:
  bool operator()(const string &s1, const string &s2) const
  { return s1.size() < s2.size(); }
};

默认情况下由 lambda 生成的类的函数调用操作符是一个const成员函数。如果lambda被声明为mutable,那么调用操作符将不是const的。

std::function

调用形式 两个不同的可调用对象拥有相同的调用形式,如:

1
2
3
4
5
6
7
8
9
10
11
// 函数
int add(int i, int j) { return i + j; }
// lambda
auto mod = [](int i, int j) { return i % j; };
// 函数对象类
struct div {
  int operator()(int denominator, int divisor) {
    return denominator / divisor;
  }
};
// 调用形式都为 int(int, int)

当想通过一个map存储这些具有相同表达形式的可调用对象时却会失败,如

1
map<string, int(*)(int, int)> binops;	// 不成功,每个 lambda 有自己的类类型

通过定义在functional头文件中的新的标准库类std::function来解决此问题

定义:

1
2
3
4
5
6
7
8
9
function<int(int, int)>	
// function 是模板,当我们创建 function 类型对象时我们必须提供额外的信息,在这里是调用签名
map<string, function<int(int, int)>> binops = {
  {"+", add}, // 函数指针
  {"-", std::minus<int>()}, // 库函数对象
  {"/", div()}, // 用户定义函数对象
  {"*", [](int i, int j) { return i * j; }}, // 匿名 lambda
  {"%", mod} // 具名 lambda
};

类型转换运算符

转换操作符(conversion operator)是一种特殊的成员函数,可以将一个类类型的值转为一个其它类型的值。

类型转换运算符必须是成员函数,并且不指定返回值类型,其参数列表必须是空的。这个函数通常应该是const 的。转换成数组或者函数类型是被禁止的。转换成指针类型(数据和函数指针)以及引用类型是允许的

形式:operatro type() const;

显式转换操作符

1
2
3
4
class SmallInt {
public:
  explicit operator int() const { return val; }
};

explicit构造函数一样,编译器不会自动运用explicit转换操作符进行隐式转换:

1
2
3
SmallInt si = 3;
si + 3; //错误:用到了隐式转换,但是重载的操作符是显式的
static_cast<int>(si) + 3; //ok:显式调用转换

但是编译器会将 explicit 转换用在条件中,如下: