成员访问运算符

来自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[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-> 的类以使其真正可用。

目录

[编辑] 解释

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

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

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

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

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

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

对于每个(可选地带有 cv 限定的)对象类型 T,有如下函数签名参与重载解析:

T& operator[](T*, std::ptrdiff_t);
T& operator[](std::ptrdiff_t, T*);

其非指针操作数可以为任何整型或无作用域枚举类型的表达式,它被隐式转换std::ptrdiff_t。表达式 E1[E2] 严格等同于表达式 *(E1 + E2),就是说,其指针操作数(可能是数组向指针转换的结果,且必须指向某个数组的元素或越过末尾位置),根据指针算术规则被调整为指向同一个数组的另一个元素,然后被解引用。

当实施于数组时,下标表达式是左值,若数组为左值,而若其不为左值则为亡值 (C++11 起)。当实施于指针时,下标表达式总是左值。

不允许类型 T不完整类型,即便并不使用 T 的大小和内部结构(如 &x[0])也是如此。

#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

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

对于每个(可能被 cv 限定的)对象类型或(没有 const 或引用限定的)函数类型 T,有如下函数签名参与重载解析:

T& operator*(T*);

内建的间接运算符的操作数为对象或函数的指针,而其结果为该指针所指向的左值。(可能被 cv 限定的)void 的指针无法被解引用。其他不完整类型的指针可以被解引用,但其结果左值仅能用于允许不完整类型的左值的语境之中,比如初始化一个引用。

#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; // 函数左值可以绑定于引用
}


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

内建的 operator& 的操作数为任何类型的左值表达式或者某个类的非静态数据成员或成员函数的限定名。这个运算符并不参与重载决议。当没有可行函数时,使用一些专门的规则:

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


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

operator. 的第一个操作数是完整类类型 T 的表达式。内建的 operator-> 的第一个操作数时指向完整类类型的指针 T* 的表达式。两种运算符的第一个操作数都被求值,即便它并不必须(比如其第二个操作数指名的是静态成员)。

两个运算符的第二个操作数是 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. 的左操作数具有非类类型的唯一情形。允许进行伪析构函数调用,使得编写代码而无需了解某个给定类型是否存在析构函数成为可能。
#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

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

operator.* 的第一个操作数是类类型 T 的表达式。operator->* 的第一个操作数时类类型的指针类型 T* 的表达式。

两种运算符的第二个操作数均为 T 的成员指针或 T 的某个无歧义且可访问的基类 B 的成员指针类型的表达式。

对于每个类型 DBR 的组合,其中类类型 B 为与 D 相同的类或为 D 的无歧义且可访问的基类,而 R 为对象或函数类型,有如下函数签名参与重载解析:

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

其中两个操作数都可能被 cv 限定,此时返回类型的 cv 限定性为两个操作数的 cv 限定性的合并。

对于内建类型,表达式 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 指向带有引用限定符 & 的成员函数时,程序非良构;
7)E1 为左值而 E2 指向带有引用限定符 && 的成员函数时,程序非良构。
#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 的公开成员函数) [edit]
提供对所管理数组的按索引访问
(std::unique_ptr 的公开成员函数) [edit]
访问指定位置的字符
(std::basic_string 的公开成员函数) [edit]
访问指定的元素
(std::array 的公开成员函数) [edit]
访问指定的元素
(std::deque 的公开成员函数) [edit]
访问指定的元素
(std::vector 的公开成员函数) [edit]
访问指定的元素
(std::map 的公开成员函数) [edit]
访问指定的元素
(std::unordered_map 的公开成员函数) [edit]
按索引访问元素
(std::reverse_iterator 的公开成员函数) [edit]
按索引访问元素
(std::move_iterator 的公开成员函数) [edit]
对 valarray 的元素、切片或遮罩进行获取/设置
(std::valarray 的公开成员函数) [edit]
返回指定的子匹配
(std::match_results 的公开成员函数) [edit]

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

对指向被管理对象的指针进行解引用
(std::unique_ptr 的公开成员函数) [edit]
对所存储的指针进行解引用
(std::shared_ptr 的公开成员函数) [edit]
访问所管理的对象
(std::auto_ptr 的公开成员函数) [edit]
对迭代器进行解引用
(std::raw_storage_iterator 的公开成员函数) [edit]
对递减后的底层迭代器进行解引用
(std::reverse_iterator 的公开成员函数) [edit]
空操作
(std::back_insert_iterator 的公开成员函数) [edit]
空操作
(std::front_insert_iterator 的公开成员函数) [edit]
空操作
(std::insert_iterator 的公开成员函数) [edit]
访问指向的元素
(std::move_iterator 的公开成员函数) [edit]
返回当前元素
(std::istream_iterator 的公开成员函数) [edit]
空操作
(std::ostream_iterator 的公开成员函数) [edit]
获取当前字符的一个副本
如果 CharT 有成员则访问当前字符的成员
(std::istreambuf_iterator 的公开成员函数) [edit]
空操作
(std::ostreambuf_iterator 的公开成员函数) [edit]
访问当前匹配
(std::regex_iterator 的公开成员函数) [edit]
访问当前子匹配
(std::regex_token_iterator 的公开成员函数) [edit]

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

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

[编辑] 缺陷报告

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

报告 适用于 已发布的行为 正确行为
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
&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 起)