成员访问运算符

来自cppreference.com
< cpp‎ | language
 
 
 
表达式
概述
值类别(左值 lvalue 、右值 rvalue 、亡值 xvalue )
求值顺序(序列点)
常量表达式
不求值表达式
初等表达式
lambda 表达式(C++11)
字面量
整数字面量
浮点字面量
布尔字面量
字符字面量,包含转义序列
字符串字面量
空指针字面量(C++11)
用户定义字面量(C++11)
运算符
赋值运算符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||b, a&&b, !a
比较运算符a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(C++20)
成员访问运算符a[b], *a, &a, a->b, a.b, a->*b, a.*b
其他运算符a(...), a,b, a?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)a, T(a)
用户定义转换
 

访问其运算数的成员。

运算符名 语法 可重载 原型示例(对于 class T
类定义中 类定义外
数组下标 a[b] R& T::operator[](const T2& b); N/A
间接(由 a 所指向的变量) *a R& T::operator*(); R& operator*(T &a);
取地址 &a R* T::operator&(); R* operator&(T &a);
对象的成员 a.b N/A N/A
指针的成员 a->b R* T::operator->() N/A
对象的成员指针 a.*b N/A N/A
指针的成员指针 a->*b R* T::operator->*(R) R* T::operator->*(T, R)
注意
  • 与大多数用户定义的重载相同,返回类型应当与内建运算符所提供的返回类型相匹配,以便用户定义的运算符可以和内建运算符以相同方式使用。不过,在用户定义的运算符重载中,任何类型都可以作为其返回类型(包括 void )。一个例外是 operator-> ,它必须返回一个指针或者另一个带有重载的 operator-> 的类以使其真正可用。

目录

[编辑] 解释

内建的下标运算符提供对其 指针数组运算数所指向的对象的访问。

内建的间接运算符提供对其指针运算数所指向的对象或函数的访问。

内建的取地址运算符创建指向其对象或函数运算数的指针。

对象的成员对象的成员指针运算符提供对其类运算数的数据成员或成员函数的访问。

内建的指针的成员指针的成员指针运算符提供对其指针运算数所指向的类的数据成员或成员指针的访问。

[编辑] 内建的下标运算符

下标运算符表达式拥有形式

expr1 [ expr2 ] (1)
expr1 [ { expr, ... } ] (2) (C++11)
1) 对于内建运算符,表达式之一( expr1expr2 )必须是“ T 的数组”类型的泛左值或“指向 T 的指针”类型的纯右值,而另一表达式(分别为 expr2expr1 )必须是无作用域枚举或整数类型的纯右值。此表达式的结果拥有 T 类型。
2) 拥有方括号内的花括号环绕列表的形式仅用于调用重载的 operator[]

内建下标表达式 E1[E2] 准确地等同于表达式 *(E1 + E2) ,即将指针运算数(可以是数组到指针转换的结果,而它必须指向某数组的元素或结尾后一位置)调整为指向同数组的另一元素,遵循指针算术的规则,再解引用。

应用到数组时,下标表达式为左值,若数组为左值,否则为亡值 (C++11 起)

应用到指针时,下标表达式始终是左值。

不允许类型 T不完整类型,即使决不使用 T 的大小或内部结构,如在 &x[0] 中。

对于用户定义运算符的重载决议中,对于每个对象类型 T (可有 cv 限定),下列函数签名参与重载决议:

T& operator[](T*, std::ptrdiff_t);
T& operator[](std::ptrdiff_t, T*);
#include <iostream>
 
int main()
{
    int a[4] = {1, 2, 3, 4};
    int* p = &a[2];
    std::cout << p[1] << p[-1] << 1[p] << (-1)[p] << '\n';
}

输出:

4242

[编辑] 内建的间接运算符

间接运算符表达式拥有形式

* expr

内建间接运算符的运算数必须是指向对象指针或指向函数指针,而结果是指针所指涉的左值,或 expr 所指向的函数。

指向(可有 cv 限定)的 void 的指针不能解引用。指向其他不完整类型的指针可以解引用,但产生的左值只能用在允许不完整类型的语境中,例如在初始化引用时。

对于用户定义运算符的重载决议中,对于每个作为对象类型(可有 cv 限定)或函数类型(非 const 或引用限定)的类型 T ,下列函数签名参与重载决议:

T& operator*(T*);
#include <iostream>
 
int f() { return 42; }
 
int main()
{
    int n = 1;
    int* pn = &n;
 
    int& r = *pn;  // 能绑定左值到引用
    int m = *pn;   // 间接 + 左值到右值转换
 
    int (*fp)() = &f;
    int (&fr)() = *fp; // 能绑定函数左值到引用
}


[编辑] 内建的取地址运算符

取址运算符表达式拥有形式

& expr (1)
& class :: member (2)
1) 当其运算数是某个对象或函数类型 T 的左值表达式时, operator& 创建并返回一个有相同 cv 限定的 T* 类型的纯右值,并指向由该运算数所代表的对象或函数。如果其运算数具有不完整类型,则能形成此指针,但若该不完整类型刚巧为某个定义其自身的 operator& 类,则其行为未定义 (C++14 前)则未指定使用内建还是重载运算符 (C++14 起)。对于带有用户定义的 operator& 的类型的运算数,可以使用 std::addressof 来获取真正的指针。
当其运算数为重载函数的名字时,仅当可以根据其语境解析这个重载时才能取得其地址。详细说明参见重载函数的地址
2) 当其运算数为非静态成员的限定名(比如 &C::member )时,其结果为 C 类中的 T 类型的成员函数的指针数据成员的指针纯右值。注意,&memberC::member ,甚至 &(C::member) 都不能用于初始化成员指针。

对于用户定义运算符的重载决议中,此运算符不引入任何额外函数签名:若存在作为可达函数的重载 operator& ,则不应用内建取址运算符。

void f(int) {}
void f(double) {}
struct A { int i; };
struct B { void f(); };
 
int main()
{
    int n = 1;
    int* pn = &n; // 指针
    int* pn2 = &*pn; // pn2 == pn
    int A::* mp = &A::i; // 数据成员的指针
    void (B::*mpf)() = &B::f; // 成员函数的指针
 
    void (*pf)(int) = &f; // 根据初始化语境进行重载决议
//  auto pf2 = &f; // 错误:重载函数类型有歧义
    auto pf2 = static_cast<void (*)(int)>(&f); // 由于转型进行重载决议
}


[编辑] 内建的成员访问运算符

成员访问运算符表达式拥有形式

expr . template(可选) id-expr (1)
expr -> template(可选) id-expr (2)
expr . pseudo-destructor (3)
expr -> pseudo-destructor (4)
1) 第一运算数必须是完整类类型 T 的表达式。
2) 第一运算数必须是指向完整类类型的指针 T* 的表达式。
3,4) 第一运算数必须是标量类型表达式(见后述)。

两种运算符的第一个运算数都被求值,即便它并不必须(比如其第二个运算数指名的是静态成员)。

两个运算符的第二个运算数是 TT 的某个无歧义且可访问的基类 B 的数据成员或成员函数的名字(正式的说法是标识表达式( id-expression ))(如 E1.E2E1->E2),并可选地有限定(如 E1.B::E2E1->B::E2),可选地使用 template 消歧义符(如 E1.template E2E1->template E2)。

如果提供了用户定义的 operator-> ,则递归地对其所返回的值再次调用 operator-> ,直到到达返回普通指针的 operator-> 为止。之后再对这个指针应用内建语义。

对于内建类型,表达式 E1->E2 严格等价于 (*E1).E2;因此以下规则仅处理了 E1.E2 的情形。

在表达式 E1.E2 中:

1)E2静态数据成员时:
  • 如果 E2 具有引用类型 T&T&&,则其结果为 T 类型的左值,代表 E2 所代表的对象或函数,
  • 否则,其结果为代表该静态数据成员的左值。
基本上,这两种情况下 E1 均被求值随即被丢弃;
2)E2非静态数据成员时:
  • 如果 E2 具有引用类型 T&T&&,则其结果为 T 类型的左值,代表 E2 所代表的对象或函数,
  • 否则,如果 E1 为左值,则其结果为代表 E1 的这个非静态数据成员的左值,
  • 否则(E1右值 (C++17 前)亡值(可能是从纯右值实体化而来) (C++17 起)),其结果为代表 E1 的这个非静态数据成员的右值 (C++11 前)亡值 (C++11 起)
E2 不是 mutable 成员,则结果的 cv 限定性E1E2 的 cv 限定性的合并,否则(E2 是 mutable 成员)为 E1E2 的 volatile 限定性的合并;
3)E2静态成员函数时,其结果为代表该静态成员函数的左值。基本上,这种情况下 E1 被求值随即被丢弃;
4)E2 为(包括析构函数在内的)非静态成员函数时,其结果为代表 E1 的这个非静态成员函数的一种特殊的纯右值,它只能用作成员函数调用运算符的左运算数,而不能用于其他目的;
5)E2 为成员枚举符时,其结果为等于 E1 的这个成员枚举符的纯右值;
6)E2嵌套类型时,程序为病式(无法编译);
7)E1标量类型E2 为一个 ~ 之后跟着代表(移除 cv 限定性后)相同类型的类型名decltype 指定符,可选地有限定时,其结果为一种特殊的纯右值,它只能用作函数调用运算符的左运算数,而不能用于其他目的。所构成的函数调用表达式被称为伪析构函数调用。它不接受任何参数,返回 void,且除了对开头的 E1 的求值之外不实施任何操作。这是 operator. 的左运算数具有非类类型的唯一情形。允许进行伪析构函数调用,使得编写代码而无需了解某个给定类型是否存在析构函数成为可能。

operator. 不能重载,而对于 operator->,在对于用户定义运算符的重载决议中,内建运算符不引入任何额外函数签名:若存在作为可达函数的重载 operator& ,则不应用内建的 operator->

#include <iostream>
 
struct P
{
    template<typename T>
    static T* ptr() { return new T; }
};
 
template<typename T>
struct A
{
    A(int n): n(n) {}
    int n;
    static int sn;
    int f() { return 10 + n; }
    static int sf() { return 4; }
    class B {};
    enum E {RED = 1, BLUE = 2};
 
    void g()
    {
        typedef int U;
        // 待决的模板成员需要关键词 template
        int* p = T().template ptr<U>();
        p->~U(); // U 为 int,调用 int 的伪析构函数
        delete p;
    }
};
template<> int A<P>::sn = 2;
 
int main()
{
    A<P> a(1);
    std::cout << a.n << ' '
              << a.sn << ' '   // A::sn 也可以工作
              << a.f() << ' ' 
              << a.sf() << ' ' // A::sf() 也可以工作
//            << a.B << ' '    // 错误:不允许嵌套类型
              << a.RED << ' '; // 枚举符
}

输出:

1 2 11 4 1

[编辑] 内建的成员指针访问运算符

通过指针的成员访问表达式拥有形式

lhs .* rhs (1)
lhs ->* rhs (2)
1) lhs 必须是类类型 T 表达式。
2) lhs 必须是指向类类型指针 T* 表达式。

两个运算符的第二运算数都是指向 T 成员指针类型(数据函数)或指向 T 的无歧义且可访问基类 B 成员指针的表达式。

对于内建类型,表达式 E1->*E2 严格等价于 (*E1).*E2 ;因此以下规则仅处理了 E1.*E2 的情形。

在表达式 E1.*E2 中:

1)E2 为指向数据成员的指针时,
  • 如果 E1 为左值,则其结果为代表这个成员的左值,
  • 否则(E1右值 (C++17 前)亡值(可能是从纯右值实质化而来) (C++17 起)),其结果为代表这个数据成员的右值 (C++11 前)亡值 (C++11 起)
2)E2 为指向成员函数的指针时,其结果为代表这个成员函数的一种特殊的纯右值,它只能用作成员函数调用运算符的左运算数,而不能用于其他目的;
3) cv 限定性的规则与对象的成员运算符相同,但有一条额外规则:指代 mutable 成员的成员指针不能用于改动 const 对象中的这个成员;
4)E2 为空成员指针值时,其行为未定义;
5)E1动态类型并不包含 E2 所指代的成员时,其行为未定义;
6)E1 为右值而 E2 指向带有引用限定符 & 的成员函数时,程序为病式,除非该成员函数亦为 const 限定但非 volatile 限定 (C++20 起)
7)E1 为左值而 E2 指向带有引用限定符 && 的成员函数时,程序为病式。

对于用户定义运算符的重载决议中,对于每个类型 DBR 的组合,其中类类型 B 是与 D 相同的类,或 D 的无歧义且可访问基类,而 R 是对象或函数类型,下列函数签名参与重载决议:

R& operator->*(D*, R B::*);

其中两个运算数都可为 cv 限定,该情况下返回类型的 cv 限定是运算数 cv 限定的联合。

#include <iostream>
 
struct S
{
    S(int n): mi(n) {}
    mutable int mi;
    int f(int n) { return mi + n; }
};
 
struct D: public S
{
    D(int n): S(n) {}
};
 
int main()
{
    int S::* pmi = &S::mi;
    int (S::* pf)(int) = &S::f;
 
    const S s(7);
//  s.*pmi = 10; // 错误:无法通过 mutable 进行修改
    std::cout << s.*pmi << '\n';
 
    D d(7); // 基类的指针可以在派生类对象上工作
    D* pd = &d;
    std::cout << (d.*pf)(7) << ' '
              << (pd->*pf)(8) << '\n';
}

输出:

7
14 15

[编辑] 标准库

许多标准容器类都重载了下标运算符

访问指定的位
(std::bitset 的公开成员函数) [编辑]
提供到被管理数组的有索引访问
(std::unique_ptr 的公开成员函数) [编辑]
访问指定字符
(std::basic_string 的公开成员函数) [编辑]
访问指定的元素
(std::array 的公开成员函数) [编辑]
访问指定的元素
(std::deque 的公开成员函数) [编辑]
访问指定的元素
(std::vector 的公开成员函数) [编辑]
访问指定的元素
(std::map 的公开成员函数) [编辑]
访问指定的元素
(std::unordered_map 的公开成员函数) [编辑]
按索引访问元素
(std::reverse_iterator 的公开成员函数) [编辑]
按索引访问元素
(std::move_iterator 的公开成员函数) [编辑]
获取/设置 valarray 数组元素、切片或掩码
(std::valarray 的公开成员函数) [编辑]
返回指定的子匹配
(std::match_results 的公开成员函数) [编辑]

许多迭代器和智能指针类都重载了间接和成员运算符

解引用指向被管理对象的指针
(std::unique_ptr 的公开成员函数) [编辑]
解引用存储的指针
(std::shared_ptr 的公开成员函数) [编辑]
访问被管理对象
(std::auto_ptr 的公开成员函数) [编辑]
解引用迭代器
(std::raw_storage_iterator 的公开成员函数) [编辑]
对递减后的底层迭代器进行解引用
(std::reverse_iterator 的公开成员函数) [编辑]
空操作
(std::back_insert_iterator 的公开成员函数) [编辑]
空操作
(std::front_insert_iterator 的公开成员函数) [编辑]
空操作
(std::insert_iterator 的公开成员函数) [编辑]
访问指向的元素
(std::move_iterator 的公开成员函数) [编辑]
返回当前元素
(std::istream_iterator 的公开成员函数) [编辑]
无操作
(std::ostream_iterator 的公开成员函数) [编辑]

(C++11 起)(C++17 前)
获得当前字符的副本
CharT 拥有成员,则访问当前字符的成员
(std::istreambuf_iterator 的公开成员函数) [编辑]
无操作
(std::ostreambuf_iterator 的公开成员函数) [编辑]
访问当前匹配
(std::regex_iterator 的公开成员函数) [编辑]
访问当前子匹配
(std::regex_token_iterator 的公开成员函数) [编辑]

标准库中的类都没有重载 operator&。最为人所知的重载了 operator& 的例子是微软的 COM 类 CComPtr,但它也会在如 boost.spirit 这样的 EDSL 中出现。

标准库中的类都没有重载 operator->*。曾有建议将其作为智能指针接口的一部分,并在 boost.phoenix 中的 actor 上有实际应用,但它在如 cpp.react 这样的 EDSL 中更常用。

[编辑] 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

DR 应用于 出版时的行为 正确行为
CWG 1213 C++11 数组右值的下标操作导致左值 重分类为亡值

[编辑] 参阅

运算符优先级

运算符重载

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

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[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 销毁先前由 new 表达式创建的对象,并释放其所拥有的内存区域
sizeof 查询类型的大小
sizeof... 查询形参包的大小(C++11 起)
typeid 查询类型的类型信息
noexcept 查询表达式是否能抛出异常(C++11 起)
alignof 查询类型的对齐要求(C++11 起)