其他运算符

来自cppreference.com
< cpp‎ | language
 
 
 
表达式
概述
值类别(左值 lvalue、右值 rvalue、亡值 xvalue)
求值顺序(序列点)
常量表达式
不求值表达式
初等表达式
lambda 表达式(C++11)
字面量
整数字面量
浮点字面量
布尔字面量
字符字面量,包含转义序列
字符串字面量
空指针字面量(C++11)
用户定义字面量(C++11)
运算符
赋值运算符a=ba+=ba-=ba*=ba/=ba%=ba&=ba|=ba^=ba<<=ba>>=b
自增与自减++a--aa++a--
算术运算符+a-aa+ba-ba*ba/ba%b~aa&ba|ba^ba<<ba>>b
逻辑运算符a||ba&&b!a
比较运算符a==ba!=ba<ba>ba<=ba>=b
成员访问运算符a[b]*a&aa->ba.ba->*ba.*b
其他运算符a(...)a,ba?b:c
运算符的替代表示形式
优先级和结合性
折叠表达式(C++17)
new 表达式
delete 表达式
throw 表达式
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
运算符重载
类型转换
隐式转换
const_cast
static_cast
reinterpret_cast
dynamic_cast
显式转换 (T)aT(a)
用户定义转换
 
运算符名 语法 可重载 原型示例(对于 class T
类定义内 类定义外
函数调用 a(a1, a2) R T::operator()(Arg1 &a1, Arg2 &a2, ... ...); N/A
逗号 a, b T2& T::operator,(T2 &b); T2& operator,(const T &a, T2 &b);
三元条件 a ? b : c N/A N/A

目录

[编辑] 解释

函数调用运算符为任何对象提供函数的语义。

三元条件运算符检查第一个表达式的布尔值,然后依据其结果值求值并返回第二个或第三个表达式。

[编辑] 内建的函数调用运算符

E(A1, A2, A3) 这样的函数调用表达式,由一个指名函数的表达式 E,跟着一个带有括号的可能为空的表达式列表 A1, A2, A3, ... 所构成。

指名函数的表达式可以是

a) 指代某个函数的左值表达式
b) 函数的指针
c) 选择某个成员函数的显式类成员访问表达式
d) 隐式类成员访问表达式,比如在另一个成员函数中所使用的成员函数的名字。

E 所指名的函数(或成员)可以被重载,使用重载解析规则来确定其所调用的是哪个重载。

E 指定了成员函数时,它可以是虚函数,这种情况下将使用运行时的动态派发以调用该函数的最终覆盖函数。

为调用该函数,

表达式 E 以及作为实参提供的 A1A2A3 等全部表达式,将以任意顺序求值,互相之间是无顺序的

(C++17 前)

表达式 E 按顺序早于各个表达式 A1A2A3 等及其各个默认实参(如果有)。各实参表达式以任意顺序求值,互相之间是顺序不确定的

(C++17 起)

各个函数形参均以其对应的(按需经过隐式转换的)实参来初始化。如果所调用的是成员函数,则当前对象的 this 指针如同以显示强制转换为该函数所预期的 this 指针一样进行转换。各个形参的初始化和销毁发生于调用方的语境中,这意味着,比如当某个形参的构造函数抛出了异常时,该函数中所定义的异常处理器,即便是其函数 try 块,都不会被考虑。当函数是变参函数时,对由省略号形参所匹配的所有实参都进行默认实参提升。形参的生存期是结束于定义它的函数返回时还是在其外围的全表达式的末尾是由实现定义的。

函数调用表达式的返回类型是所选择的函数的返回类型,取决于其静态绑定(忽略 virtual 关键字),即便其所实际调用的覆盖函数返回了不同的类型也是如此。允许覆盖函数所返回某个类的指针或引用,该类派生于基类函数的返回类型,也就是说,C++ 支持协变(covariant)返回类型。当 E 指定了析构函数时,其返回类型为 void

当向函数传递或从函数返回类类型 X 的对象时,如果 X 的复制构造函数、移动构造函数和析构函数要么是平凡的(trivial),要么被弃置(deleted),且 X 具有至少一个未弃置的复制或移动构造函数,则实现可以创建一个临时对象以持有这个函数形参或返回对象。

这个临时对象是分别以函数实参或返回值进行构造的,并且函数的形参或返回对象如同使用这个未弃置的平凡构造函数来复制这个临时对象一样进行初始化的(即便该构造函数不可访问或者不会被重载解析所选择以实施该对象的复制或移动操作也是如此)。

这就允许小型的类类型,如 std::complexgsl::span,可以在寄存器中传递给函数或从函数返回。

(C++17 起)

函数调用表达式的值类别,当函数返回左值引用或函数的右值引用时为左值,当函数返回对象的右值引用时为亡值,否则为纯右值。当函数调用表达式是对象类型的纯右值时,除非该纯右值并未被实体化,比如 (C++17 起)用作 decltype 的操作数(或用作作为 decltype 的操作数的内建逗号运算符表达式的右操作数),否则必须具有完整类型。

函数调用表达式,在语法上和值初始化 T()函数风格的强制转换表达式 T(A1),以及临时变量的直接初始化 T(A1, A2, A3, ...) 都很相似,其中 T 为某个类型的名字。

#include <cstdio>
struct S
{
    int f1(double d) {
        printf("%f \n", d); // 变参函数调用
    }
    int f2() {
        f1(7); // 成员函数调用,与 this->f1() 相同
               // 整数实参被转换为 double
    }
};
void f() {
   puts("function called"); // 函数调用
}
int main()
{
    f(); // 函数调用
    S s;
    s.f2(); // 成员函数调用
}

输出:

function called
7.000000

[编辑] 内建的逗号运算符

在逗号表达式 E1, E2 中,表达式 E1 被求值,其结果被丢弃,而其副作用则在对表达式 E2 的求值开始前完成(注意,用户定义的 operator, 并不保证顺序性) (C++17 前)

逗号表达式的类型、值和值类别和其第二个操作数 E2 的类型、值和值类别完全相同。当 E2 是临时对象表达式 (C++17 起)时,该表达式的结果就是该临时对象表达式 (C++17 起)。当 E2 是位域时,其结果为位域。

各种逗号分隔列表(如函数实参列表 f(a, b, c) 和初始化式列表 int a[] = {1,2,3})中的逗号,都不是逗号运算符。如果需要在这些语境中使用逗号运算符,则需要给它加上括号:f(a, (n++, n+b), c)

#include <iostream>
int main()
{
    int n = 1;
    int m = (++n, std::cout << "n = " << n << '\n', ++n, 2*n);
    std::cout << "m = " << (++m, m) << '\n';
}

输出:

n = 2
m = 7

[编辑] 条件运算符

条件运算符的第一个操作数被求值并被按语境转换bool。于第一个操作数的值计算和副作用均完成之后,若器结果为 true,则求值第二个操作数。若其结果为 false,则求值第三个操作数。

条件表达式 E1 ? E2 : E3 的类型和值类别根据下列规则确定:

1) 如果 E2E3 有任何一个类型为 void,则以下之一必须为真,否则程序非良构:
1.1) E2E3 之一(但并非都)为(可能有括号的)throw 表达式。条件运算符的结果具有另一个表达式的类型和值类别。如果另一个表达式是位域,则其结果为位域。这种条件运算符在 C++14 之前的 C++11 constexpr 开发 中很常用。
1.2) E2E3 的类型均为 void(包括二者均为 throw 表达式的情况)。结果为 void 类型的纯右值。
2) 否则,如果 E2E3 为具有相同值类别的泛左值位域,类型分别为 cv1 T 和 cv2 T,则在本小节的余下部分中将两个操作数都当做具有类型 cv T,其中 cv 为 cv1 和 cv2 的合并。
(C++14 起)
3) 否则,如果 if E2E3 具有不同类型,其中至少一个是(可能被 cv 限定的)类类型,或者二者均为相同值类别的泛左值并具有除 cv 限定性外相同的类型,则尝试从每个操作数向由另一个操作数所确定的目标类型构成一个隐式转换序列 (忽略成员访问性,是否是位域,以及转换函数是否被弃置) (C++14 起),如下所述。TX 类型的操作数 X 可以以如下方式转换为 TY 类型的操作数 Y目标类型
3.1)Y 为左值时,目标类型为 TY&,且引用必须直接绑定于左值;
3.2)Y 为亡值时,目标类型为 TY&&,且引用必须直接绑定;
3.3)Y 为纯右值,或以上的转换序列均无法构成而 TXTY 中至少一个是(可能被 cv 限定的)类类型时,目标类型为 Y 经过左值向右值、数组向指针和函数向指针标准转换之后所具有的类型;
3.4) 如果两个转换序列(E2 向 E3 的目标类型和 E3 向 E2 的目标类型)均可被构成,或者只能构成一个但它是有歧义的转换序列,则程序非良构。
3.5) 如果恰有一个转换序列可以被构成(注意,它也许仍然非良构,比如违反访问性),则实施这个转换序列并在这部分说明(从 (4) 开始)的余下部分中以转换后的操作数替换原来的操作数
3.6) 如果无法构成转换序列,则在这部分说明的余下部分中不改变两个操作数
4) 如果 E2E3 均为相同类型和相同值类别的泛左值,则其结果具有相同的类型和值类别,并且当 E2E3 至少有一个是位域时为位域。
5) 否则,结果为纯右值。如果 E2E3 类型不相同,且有任何一个是(可能被 cv 限定的)类类型,则实施重载解析并使用下述的内建候选以尝试将两个操作数转换为内建类型。如果重载解析失败,则程序非良构。否则,实施所选择的转换并在第 6 步中使用转换后的操作数来替代原来的操作数。
6) 在第二和第三个操作数上实施左值向右值、数组向指针和函数向指针转换。然后,
6.1) 如果 E2E3 均已经为相同类型,则结果为该类型的纯右值,代表一个临时对象并 (C++17 前)其结果对象 (C++17 起)以求值 E1 之后所选择的操作数进行复制初始化。
6.2) 如果 E2E3 均为算术或枚举类型:实施一般算术转换以将它们变为其公共类型,并以该类型为结果。
6.3) 如果 E2E3 均为指针,或其中之一为指针而另一个为空指针常量,则实施指针转换和限定性转换以将它们变为其公共类型,并以该类型为结果。
6.4) 如果 E2E3 均为成员指针,或其中之一为成员指针而另一个为空指针常量,则实施成员指针转换和限定性转换以将它们变为其公共类型,并以该类型为结果。
6.5) 如果 E2E3 均为空指针常量,且至少一个的类型为 std::nullptr_t,则结果类型为 std::nullptr_t
6.6) 所有其他的情况,程序均非良构。

对于每一对提升后的算术类型 L 和 R 以及每个类型 P,其中 P 为指针、成员指针或有作用域枚举类型,有如下函数签名参与以上规则的第 5 步中所实施的重载解析:

LR operator?:(bool, L, R );
P operator?:(bool, P, P );

其中 LR 为对 LR 进行一般算术转换后的结果。运算符 “?:” 无法充值,这些函数签名仅用于重载解析而存在。

条件运算符的返回类型还可以通过二元类型特征 std::common_type 来得到。

#include <string>
#include <stdexcept>
#include <iostream>
struct Node
{
    Node* next;
    int data;
    // 深拷贝的复制构造函数
    Node(const Node& other)
      : next(other.next ? new Node(*other.next) : NULL)
      , data(other.data)
    {}
    Node(int d) : next(NULL), data(d) {}
    ~Node() { delete next ; }
};
int main()
{   
    // 简单的右值例子
    int n = 1>2 ? 10 : 11;  // 1>2 为 false,因而 n = 11
    // 简单的左值例子
    int m = 10; 
    (n == m ? n : m) = 7; // n == m 为 false,因而 m = 7
    // throw 的例子
    std::string str = 2+2==4 ? "ok" : throw std::logic_error("2+2 != 4");
    std::cout << "n = " << n << "\nm = " << m << "\nstr = " << str; //输出结果
}

输出:

n = 11
m = 7
str = ok

[编辑] 标准库

标准库中的许多类都重载了 operator() 以便用作函数对象。

删除对象或数组
(std::default_delete 的公开成员函数) [edit]
返回两个实参的和
(std::plus 的公开成员函数) [edit]
返回两个实参的差
(std::minus 的公开成员函数) [edit]
返回两个实参的乘积
(std::multiplies 的公开成员函数) [edit]
返回第一个实参除以第二个实参的结果
(std::divides 的公开成员函数) [edit]
返回第一个实参除以第二个实参的余数
(std::modulus 的公开成员函数) [edit]
返回其实参的非
(std::negate 的公开成员函数) [edit]
检查实参是否相等
(std::equal_to 的公开成员函数) [edit]
检查实参是否不相等
(std::not_equal_to 的公开成员函数) [edit]
检查第一个实参是否大于第二个实参
(std::greater 的公开成员函数) [edit]
检查第一个实参是否小于第二个实参
(std::less 的公开成员函数) [edit]
检查第一个实参是否大于或等于第二个实参
(std::greater_equal 的公开成员函数) [edit]
检查第一个实参是否小于或等于第二个实参
(std::less_equal 的公开成员函数) [edit]
返回两个实参的逻辑 AND
(std::logical_and 的公开成员函数) [edit]
返回两个实参的逻辑 OR
(std::logical_or 的公开成员函数) [edit]
返回其实参的逻辑 NOT
(std::logical_not 的公开成员函数) [edit]
返回两个实参的按位 AND 的结果
(std::bit_and 的公开成员函数) [edit]
返回两个实参的按位 OR 的结果
(std::bit_or 的公开成员函数) [edit]
返回两个实参的按位 XOR 的结果
(std::bit_xor 的公开成员函数) [edit]
返回对其所存储的谓词调用的结果的逻辑补
(std::unary_negate 的公开成员函数) [edit]
返回对其所存储的谓词的调用的结果的逻辑补
(std::binary_negate 的公开成员函数) [edit]
调用其所存储的函数
(std::reference_wrapper 的公开成员函数) [edit]
调用其目标
(std::function 的公开成员函数) [edit]
使用这个 locale 的 collate 刻面按字典序比较两个字符串
(std::locale 的公开成员函数) [edit]
比较两个 value_type 类型的值
(std::map::value_compare 的公开成员函数) [edit]
比较两个 value_type 类型的值
(std::multimap::value_compare 的公开成员函数) [edit]
执行函数
(std::packaged_task 的公开成员函数) [edit]
推进引擎的状态并返回其所生成的值
(std::linear_congruential_engine 的公开成员函数) [edit]
生成这个分布中的下一个随机数
(std::uniform_int_distribution 的公开成员函数) [edit]

标准库中没有任何类重载了逗号运算符。boost 库的 boost.assign,boost.spirit 和其他库中使用了 operator,。数据库访问库 SOCI 也重载了 operator,

[编辑] 缺陷报告

以下行为变化的缺陷报告可追溯至以前发布的 C++ 标准。

报告 适用于 已发布的行为 正确行为
CWG 1550 C++98 当 ?: 的另一个操作数不是 void 时不允许使用带括号的 throw 表达式 允许带括号的 throw 表达式
CWG 1560 C++98  ?: 的 void 操作数导致对另一个操作数无必要的左值向右值转换,
始终产生右值
带有 void 的 ?: 可以为左值
CWG 1932 C++14  ?: 中未说明同类型的位域 按其底层类型处理
CWG 1895 C++14 不清楚弃置的或不可访问的转换函数是否妨碍 ?: 中的转换,
并且未考虑到从基类向派生类纯右值的转换
以重载解析的方式处理

[编辑] 另请参阅

运算符优先级

运算符重载

普通运算符
赋值 自增
自减
算术 逻辑 比较 成员访问 其他

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b

a[b]
*a
&a
a->b
a.b
a->*b
a.*b

a(...)
a, b
? :

特殊运算符

static_cast 将一个类型转换为另一个相关类型
dynamic_cast 在继承层次中进行转换
const_cast 添加或移除 cv 限定符
reinterpret_cast 将类型转换为无关的类型
C 风格的强制转换通过混合 static_castconst_castreinterpret_cast 将一个类型转换为另一个类型
new 分配内存
delete 回收内存
sizeof 查询类型的大小
sizeof... 查询形参包的大小(C++11 起)
typeid 查询类型的类型信息
noexcept 检查表达式是否会抛出异常(C++11 起)
alignof 查询类型的对齐要求(C++11 起)