命名空间
变体
操作

成员访问运算符

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

访问其操作数的成员。

  运算符名称             语法           可重 原型示例 (对于 class T)
类定义内部 类定义外部
下标 a[b] R& T::operator[](S b); N/A
a[...] (自 C++23 起) R& T::operator[](...);
间接引用 *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->*(S b); R& operator->*(T a, S b);
注释
  • 与大多数用户定义的重载一样,返回类型应与内置运算符提供的返回类型匹配,以便用户定义的运算符可以像内置运算符一样使用。但是,在用户定义的运算符重载中,任何类型都可以用作返回类型(包括 void)。一个例外是 operator->,它必须返回指针或另一个具有重载 operator-> 的类,以便在实际使用中可行。

内容

[编辑] 解释

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

内置间接引用运算符提供对指针操作数指向的对象或函数的访问。

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

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

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

[编辑] 内置下标运算符

下标运算符表达式具有以下形式

expr1 [expr2 ] (1)
expr1 [{expr , ...}] (2) (自 C++11 起)
expr1 [expr2 , expr , ...] (3) (自 C++23 起)
1) 对于内置运算符,表达式之一(expr1expr2)必须是 “T 数组” 类型的 glvalue 或 “指向 T 的指针” 类型的 prvalue,而另一个表达式(分别为 expr2expr1)必须是无作用域枚举或整型类型的 prvalue。此表达式的结果类型为 Texpr2 不能是未加括号的 逗号表达式(自 C++23 起)
2) 方括号内带花括号括起来的列表形式仅用于调用重载的 operator[]
3) 方括号内带逗号分隔表达式列表的形式仅用于调用重载的 operator[]

内置下标表达式 E1[E2] 与表达式 *(E1 + E2) 完全相同,除了它的值类别(见下文)求值顺序(自 C++17 起):指针操作数(可能是数组到指针转换的结果,并且必须指向某个数组的元素或超出末尾一个位置)被调整为指向同一数组的另一个元素,遵循 指针算术规则,然后被解引用。

当应用于数组时,下标表达式是一个 左值 (如果数组是左值),并且如果不是,则为 xvalue(自 C++11 起)

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

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

使用未加括号的 逗号表达式 作为下标运算符的第二个(右)参数已被弃用。

例如,a[b, c] 已被弃用,而 a[(b, c)] 则没有。

(自 C++20 起)
(直到 C++23)

未加括号的 逗号表达式 不能作为下标运算符的第二个(右)参数。例如,a[b, c] 要么是非良构的,要么等效于 a.operator[](b, c)

需要使用括号才能将逗号表达式用作下标,例如 a[(b, c)]

(自 C++23 起)

在针对用户定义的运算符进行 重载决议中,对于每个对象类型 T(可能带有 cv 限定),以下函数签名参与重载决议

T& operator[](T*, std::ptrdiff_t);
T& operator[](std::ptrdiff_t, T*);
#include <iostream>
#include <map>
#include <string>
 
int main()
{
    int a[4] = {1, 2, 3, 4};
    int* p = &a[2];
    std::cout << p[1] << p[-1] << 1[p] << (-1)[p] << '\n';
 
    std::map<std::pair<int, int>, std::string> m;
    m[{1, 2}] = "abc"; // uses the [{...}] version
}

输出

4242

[编辑] 内置间接引用运算符

间接引用运算符表达式具有以下形式

*expr

内置间接引用运算符的操作数必须是指向对象的指针或指向函数的指针,结果是引用 expr 指向的对象或函数的左值。如果 expr 实际上并未指向对象或函数,则行为是未定义的(typeid 指定的情况除外)。

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

在针对用户定义的运算符进行 重载决议中,对于每种类型 T,如果 T 是对象类型(可能带有 cv 限定)或函数类型(不带 const 或 ref 限定),则以下函数签名参与重载决议

T& operator*(T*);
#include <iostream>
 
int f() { return 42; }
 
int main()
{
    int n = 1;
    int* pn = &n;
 
    int& r = *pn; // lvalue can be bound to a reference
    int m = *pn;  // indirection + lvalue-to-rvalue conversion
 
    int (*fp)() = &f;
    int (&fr)() = *fp; // function lvalue can be bound to a reference
 
    [](...){}(r, m, fr); // removes possible "unused variable" warnings
}

[编辑] 内置取地址运算符

取地址运算符表达式具有以下形式

&expr (1)
&class ::member (2)
1) 如果操作数是某个对象或函数类型 T 的左值表达式,则 operator& 创建并返回类型为 T* 的 prvalue,其 cv 限定与操作数相同,并指向操作数指定的对象或函数。如果操作数具有不完整类型,则可以形成指针,但如果该不完整类型恰好是一个定义了自己的 operator& 的类,则不指定是使用内置运算符还是重载运算符。对于具有用户定义的 operator& 类型的操作数,可以使用 std::addressof 来获取真正的指针。请注意,与 C99 及更高版本的 C 版本不同,对于应用于一元 operator* 结果的一元 operator&,没有特殊情况。
如果操作数是重载函数的名称,则只有当重载可以由于上下文而解析时,才可以获取地址。有关详细信息,请参见 重载函数的地址

如果 expr 命名一个显式对象成员函数,则 expr 必须是限定标识符。将 & 应用于命名显式对象成员函数的非限定标识符是非良构的。

(自 C++23 起)
2) 如果操作数是非静态成员或 变体 成员的限定名称显式对象成员函数除外)(自 C++23 起),例如 &C::member,则结果是类 C 中类型为 T 的 prvalue 成员函数指针数据成员指针。请注意,&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;    // pointer
    int* pn2 = &*pn; // pn2 == pn
 
    int A::* mp = &A::i;      // pointer to data member
    void (B::*mpf)() = &B::f; // pointer to member function
 
    void (*pf)(int) = &f; // overload resolution due to initialization context
//  auto pf2 = &f; // error: ambiguous overloaded function type
    auto pf2 = static_cast<void (*)(int)>(&f); // overload resolution due to cast
}

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

成员访问运算符表达式具有以下形式

expr .template(可选) id-expr (1)
expr ->template(可选) id-expr (2)
expr .伪析构函数 (3)
expr ->伪析构函数 (4)
1) expr 必须是 完整类类型 T 的表达式。
如果 id-expr 命名一个 静态成员枚举器,则 expr 是一个 弃值表达式
2) expr 必须是指向完整类类型 T* 的指针的表达式。
3,4) expr 必须是标量类型的表达式(见下文)。

id-exprTT 的明确且可访问的基类 B 的数据成员或成员函数的名称(形式上,一个 标识符表达式,命名),(例如 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&&(自 C++11 起),则结果是指定引用绑定的对象或函数的 T 类型的左值。
  • 否则,给定 E2 的类型为 T,则结果是指定该静态数据成员的 T 类型的左值。
本质上,E1 在这两种情况下都会被求值并丢弃。
2) 如果 E2 是一个 非静态数据成员
  • 如果 E2 的类型是引用类型 T& T&&(自 C++11 起),则结果是指定 E1 的相应引用成员绑定的对象或函数的 T 类型的左值。
  • 否则,如果 E1 是左值,则结果是指定 E1 的非静态数据成员的左值。
  • 否则(如果 E1右值(直到 C++17)xvalue(可能是从 prvalue 物化 而来)(自 C++17 起)),则结果是指定 E1 的非静态数据成员的 右值(直到 C++11)xvalue(自 C++11 起)
如果 E2 不是 mutable 成员,则结果的 cv 限定E1E2 的 cv 限定的并集,否则(如果 E2 是 mutable 成员),则是 E1E2 的 volatile 限定的并集。
3) 如果 E2 是重载集(由一个或多个静态成员函数非静态成员函数组成),则 E1.E2 必须是成员函数调用运算符的(可能带括号的)左侧运算数,并且使用函数重载决议来选择 E2 所指代的函数,之后
  • 如果 E2 是一个静态成员函数,则结果是一个指代该静态成员函数的左值。本质上,在这种情况下,E1 会被求值并丢弃。
  • 否则(E2 是一个非静态成员函数),结果是一个指代 E1 的非静态成员函数的纯右值。
4) 如果 E2 是一个成员枚举项,给定 E2 的类型为 T,则结果是 一个右值(直到 C++11)一个纯右值(自 C++11 起),类型为 T,其值是枚举项的值。
5) 如果 E2 是一个嵌套类型,则程序是非良构的。
6) 如果 E1 具有标量类型 并且 E2~ 后跟类型名decltype 说明符,指代相同类型(减去 cv 限定),可选地限定,则结果是一种特殊的纯右值,只能用作函数调用运算符的左侧运算数,不能用于其他目的
结果函数调用表达式称为伪析构函数调用。它不接受任何参数,返回 void,求值 E1,并结束其结果对象的生命周期。这是 operator. 的左侧运算数具有非类类型的唯一情况。允许伪析构函数调用使得编写代码时无需知道给定类型是否存在析构函数。

operator. 不能被重载,而对于 operator->,在针对用户定义的运算符的重载决议中,内置运算符不引入任何额外的函数签名:如果存在重载的 operator-> 并且是可行函数,则内置的 operator-> 不适用。

#include <cassert>
#include <iostream>
#include <memory>
 
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;
 
        // keyword template needed for a dependent template member
        int* p = T().template ptr<U>();
        p->~U(); // U is int, calls int's pseudo destructor
        delete p;
    }
};
 
template<>
int A<P>::sn = 2;
 
struct UPtrWrapper
{
    std::unique_ptr<std::string> uPtr;
    std::unique_ptr<std::string>& operator->() { return uPtr; }
};
 
int main()
{
    A<P> a(1);
    std::cout << a.n << ' '
              << a.sn << ' '   // A::sn also works
              << a.f() << ' ' 
              << a.sf() << ' ' // A::sf() also works
//            << &a.f << ' '   // error: ill-formed if a.f is not the
                               // left-hand operand of operator()
//            << a.B << ' '    // error: nested type not allowed
              << a.RED << ' '; // enumerator
 
    UPtrWrapper uPtrWrap{std::make_unique<std::string>("wrapped")};
    assert(uPtrWrap->data() == uPtrWrap.operator->().operator->()->data());
}

输出

1 2 11 4 1

如果 E2 是非静态成员,并且 E1 的结果是一个对象,其类型与 E1 的类型不相似,则行为是未定义的

struct A { int i; };
struct B { int j; };
struct D : A, B {};
 
void f()
{
    D d;
    static_cast<B&>(d).j;      // OK, object expression designates the B subobject of d
    reinterpret_cast<B&>(d).j; // undefined behavior
}

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

通过成员指针的成员访问运算符表达式具有以下形式

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

rhs 必须是指向 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 限定规则与对象成员运算符相同,但有一个额外的规则:指向可变成员的成员指针不能用于修改 const 对象中的该成员;
4) 如果 E2 是空成员指针值,则行为是未定义的;
5) 如果结果 E1 是一个对象,使得其类型与 E1 的类型不相似,或者其最派生对象不包含 E2 所指代的成员,则行为是未定义的;
6) 如果 E1 是右值,并且 E2 指向具有引用限定符 & 的成员函数,则程序是非良构的,除非成员函数具有 cv 限定符 const 但没有 volatile(自 C++20 起)
7) 如果 E1 是左值,并且 E2 指向具有引用限定符 && 的成员函数,则程序是非良构的。
(自 C++11 起)

在针对用户定义的运算符的重载决议中,对于类型 DBR 的每种组合,其中类类型 BD 是相同的类,或者是 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; // error: cannot modify through mutable
    std::cout << s.*pmi << '\n';
 
    D d(7); // base pointers work with derived object
    D* pd = &d;
    std::cout << (d.*pf)(7) << ' '
              << (pd->*pf)(8) << '\n';
}

输出

7
14 15

[编辑] 标准库

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

访问特定位
std::bitset<N> 的公共成员函数) [编辑]
提供对托管数组的索引访问
std::unique_ptr<T,Deleter> 的公共成员函数) [编辑]
访问指定的字符
std::basic_string<CharT,Traits,Allocator> 的公共成员函数) [编辑]
访问指定的元素
std::array<T,N> 的公共成员函数) [编辑]
访问指定的元素
std::deque<T,Allocator> 的公共成员函数) [编辑]
访问指定的元素
std::vector<T,Allocator> 的公共成员函数) [编辑]
访问或插入指定的元素
std::map<Key,T,Compare,Allocator> 的公共成员函数) [编辑]
访问或插入指定的元素
std::unordered_map<Key,T,Hash,KeyEqual,Allocator> 的公共成员函数) [编辑]
通过索引访问元素
std::reverse_iterator<Iter> 的公共成员函数) [编辑]
通过索引访问元素
std::move_iterator<Iter> 的公共成员函数) [编辑]
获取/设置 valarray 元素、切片或掩码
std::valarray<T> 的公共成员函数) [编辑]
返回指定的子匹配项
std::match_results<BidirIt,Alloc> 的公共成员函数) [编辑]

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

解引用指向托管对象的指针
std::unique_ptr<T,Deleter> 的公共成员函数) [编辑]
解引用存储的指针
std::shared_ptr<T> 的公共成员函数) [编辑]
访问托管对象
std::auto_ptr<T> 的公共成员函数) [编辑]
解引用迭代器
std::raw_storage_iterator<OutputIt,T> 的公共成员函数) [编辑]
解引用递减后的底层迭代器
std::reverse_iterator<Iter> 的公共成员函数) [编辑]
空操作
std::back_insert_iterator<Container> 的公共成员函数) [编辑]
空操作
std::front_insert_iterator<Container> 的公共成员函数) [编辑]
空操作
std::insert_iterator<Container> 的公共成员函数) [编辑]
访问指向的元素
std::move_iterator<Iter> 的公共成员函数) [编辑]
返回当前元素
std::istream_iterator<T,CharT,Traits,Distance> 的公共成员函数) [编辑]
空操作
std::ostream_iterator<T,CharT,Traits> 的公共成员函数) [编辑]
获取当前字符的副本
std::istreambuf_iterator<CharT,Traits> 的公共成员函数) [编辑]
空操作
std::ostreambuf_iterator<CharT,Traits> 的公共成员函数) [编辑]
访问当前匹配项
std::regex_iterator<BidirIt,CharT,Traits> 的公共成员函数) [编辑]
访问当前子匹配项
std::regex_token_iterator<BidirIt,CharT,Traits> 的公共成员函数) [编辑]

没有标准库类重载 operator&。重载 operator& 最著名的例子是 Microsoft COM 类 CComPtr,尽管它也可能出现在 EDSLs 中,例如 boost.spirit

没有标准库类重载 operator->*。有人建议它应该成为 智能指针接口 的一部分,事实上,boost.phoenix 中的 actors 在某种程度上使用了它,但在 EDSLs 中更常见,例如 cpp.react

[编辑] 注解

特性测试宏 Std 特性
__cpp_multidimensional_subscript 202110L (C++23) 多维下标运算符

[编辑] 缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
CWG 1213 C++11 对数组右值进行下标操作会产生左值 重新分类为将亡值
CWG 1458 C++98 & 应用于不完整类类型的左值,该类型
声明了 operator& 会导致未定义行为
它是未指明的
使用哪个 &
CWG 1642 C++98 内置成员指针访问运算符中的 rhs  可以是左值 只能是右值
CWG 1800 C++98 当将 & 应用于
成员匿名联合的非静态数据成员时,不清楚
匿名联合是否参与结果类型
匿名联合
不包含在
结果类型中
CWG 2614 C++98 如果 E2 是引用成员或枚举项,则 E1.E2 的结果不明确 已明确
CWG 2725 C++98 如果 E2 是静态成员函数,则 E1.E2 是良构的
即使它不是 operator() 的左侧运算数
E1.E2 在这种情况下是非良构的
在这种情况下
CWG 2748 C++98 如果 E1 是空指针并且 E2 指的是静态成员,则 E1->E2 的行为不明确
空指针和 E2 指的是静态成员
在这种情况下,行为是
未定义的
CWG 2813 C++98 如果 E1.E2 命名静态成员或枚举,则 E1 不是弃值表达式
E1.E2 命名静态成员或枚举
它是
CWG 2823 C++98 如果 expr 不指向对象或函数,则 *expr 的行为不明确
不指向对象或函数
已明确

[编辑] 参见

运算符优先级

运算符重载

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

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
a->b
a.b
a->*b
a.*b

函数调用

a(...)
逗号

a, b
条件

a ? b : c
特殊运算符

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 起)

C 文档 关于 成员访问运算符