Member access operators
Accède à un membre de son opérande.
| Nom de l'opérateur | Syntaxe | Surcharge able | Exemples de prototypes (pour class T ) | |
|---|---|---|---|---|
| Définition interne à la classe | Définition externe à la classe | |||
| indice | a [ b ] | Oui | R & T :: operator [ ] ( S b ) ; | N/A |
| a [ ... ] (depuis C++23) | R & T :: operator [ ] ( ... ) ; | |||
| indirection | * a | Oui | R & T :: operator * ( ) ; | R & operator * ( T a ) ; |
| adresse de | & a | Oui | R * T :: operator & ( ) ; | R * operator & ( T a ) ; |
| membre d'objet | a. b | Non | N/A | N/A |
| membre de pointeur | a - > b | Oui | R * T :: operator - > ( ) ; | N/A |
| pointeur vers membre d'objet | a. * b | Non | N/A | N/A |
| pointeur vers membre de pointeur | a - > * b | Oui | R & T :: operator - > * ( S b ) ; | R & operator - > * ( T a, S b ) ; |
|
||||
Table des matières |
Explication
L'opérateur subscript intégré fournit l'accès à un objet pointé par l'opérande pointeur ou tableau .
L'opérateur d' indirection intégré fournit l'accès à un objet ou une fonction pointé(e) par l'opérande pointeur.
L'opérateur intégré address-of crée un pointeur pointant vers l'objet ou la fonction opérande.
Membre d'objet et pointeur vers membre d'objet - ces opérateurs permettent d'accéder à un membre de données ou à une fonction membre de l'opérande objet.
Les opérateurs intégrés member of pointer et pointer to member of pointer permettent d'accéder à un membre de données ou à une fonction membre de la classe pointée par l'opérande pointeur.
Opérateur d'indice intégré
Les expressions d'opérateur d'indice ont la forme
expr1
[
expr2
]
|
(1) | ||||||||
expr1
[{
expr
, ...
}]
|
(2) | (depuis C++11) | |||||||
expr1
[
expr2
,
expr
, ...
]
|
(3) | (depuis C++23) | |||||||
T
» ou une prvalue de type « pointeur vers
T
», tandis que l'autre expression (
expr2
ou
expr1
, respectivement) doit être une prvalue de type énumération non scopée ou type intégral. Le résultat de cette expression a le type
T
.
expr2
ne peut pas être une
expression virgule
non parenthésée.
(depuis C++23)
L'expression d'indice intégrée E1 [ E2 ] est exactement identique à l'expression * ( E1 + E2 ) sauf pour sa catégorie de valeur (voir ci-dessous) et l' ordre d'évaluation (depuis C++17) : l'opérande pointeur (qui peut résulter d'une conversion tableau-vers-pointeur, et qui doit pointer vers un élément d'un tableau ou un élément au-delà de la fin) est ajusté pour pointer vers un autre élément du même tableau, suivant les règles de l' arithmétique des pointeurs , puis est déréférencé.
Lorsqu'il est appliqué à un tableau, l'expression d'indice est une lvalue si le tableau est une lvalue, et une xvalue s'il ne l'est pas (depuis C++11) .
Lorsqu'il est appliqué à un pointeur, l'expression en indice est toujours une lvalue.
Le type
T
ne doit pas être un
type incomplet
, même si la taille ou la structure interne de
T
n'est jamais utilisée, comme dans
&
x
[
0
]
.
|
L'utilisation d'une expression virgule non parenthésée comme second argument (droit) d'un opérateur d'indice est dépréciée. Par exemple, a [ b, c ] est déprécié et a [ ( b, c ) ] ne l'est pas. |
(depuis C++20)
(jusqu'à C++23) |
|
Une expression virgule non parenthésée ne peut pas être le second argument (droit) d'un opérateur d'indice. Par exemple, a [ b, c ] est soit mal formé soit équivalent à a. operator [ ] ( b, c ) . Des parenthèses sont nécessaires pour utiliser une expression virgule comme indice, par exemple, a [ ( b, c ) ] . |
(depuis C++23) |
Dans la
résolution de surcharge pour les opérateurs définis par l'utilisateur
, pour chaque type d'objet
T
(éventuellement qualifié cv), la signature de fonction suivante participe à la résolution de surcharge :
|
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"; // utilise la version [{...}] }
Sortie :
4242
Opérateur d'indirection intégré
Les expressions d'opérateur d'indirection ont la forme
*
expr
|
|||||||||
L'opérande de l'opérateur de déréférencement intégré doit être un pointeur vers un objet ou un pointeur vers une fonction, et le résultat est la lvalue référençant l'objet ou la fonction vers lequel
expr
pointe. Si
expr
ne pointe pas réellement vers un objet ou une fonction, le comportement est indéfini (sauf dans le cas spécifié par
typeid
).
Un pointeur vers (éventuellement cv -qualifié) void ne peut pas être déréférencé. Les pointeurs vers d'autres types incomplets peuvent être déréférencés, mais la lvalue résultante ne peut être utilisée que dans des contextes qui autorisent une lvalue de type incomplet, par exemple lors de l'initialisation d'une référence.
Dans
la résolution de surcharge pour les opérateurs définis par l'utilisateur
, pour chaque type
T
qui est soit un type objet (éventuellement qualifié cv) soit un type fonction (non qualifié const ou ref), la signature de fonction suivante participe à la résolution de surcharge :
|
T
&
operator
*
(
T
*
)
;
|
||
#include <iostream> int f() { return 42; } int main() { int n = 1; int* pn = &n; int& r = *pn; // une lvalue peut être liée à une référence int m = *pn; // indirection + conversion lvalue-vers-rvalue int (*fp)() = &f; int (&fr)() = *fp; // une lvalue de fonction peut être liée à une référence [](...){}(r, m, fr); // supprime les éventuels avertissements "variable inutilisée" }
Opérateur d'adresse intégré
Les expressions d'opérateur d'adresse ont la forme
&
expr
|
(1) | ||||||||
&
class
::
member
|
(2) | ||||||||
T
,
operator&
crée et retourne une prvalue de type
T*
, avec la même qualification cv, qui pointe vers l'objet ou la fonction désigné(e) par l'opérande. Si l'opérande a un type incomplet, le pointeur peut être formé, mais si ce type incomplet s'avère être une classe qui définit son propre
operator
&
, il n'est pas spécifié si la version intégrée ou la surcharge est utilisée. Pour les opérandes de type avec
operator
&
défini par l'utilisateur,
std::addressof
peut être utilisé pour obtenir le vrai pointeur.
Notez que, contrairement à C99 et aux versions ultérieures de C, il n'y a pas de cas particulier pour l'opérateur unaire
operator
&
appliqué au résultat de l'opérateur unaire
operator
*
.
|
Si
expr
désigne une
fonction membre d'objet explicite
,
expr
doit être un
identifiant qualifié
. L'application de
|
(depuis C++23) |
T
dans la classe
C
. Notez que ni
&
member
ni
C
::
member
ni même
&
(
C
::
member
)
ne peuvent être utilisés pour initialiser un pointeur vers membre.
Dans la résolution de surcharge pour les opérateurs définis par l'utilisateur , cet opérateur n'introduit aucune signature de fonction supplémentaire : l'opérateur d'adresse intégré ne s'applique pas s'il existe une surcharge de operator & qui est une fonction viable .
void f(int) {} void f(double) {} struct A { int i; }; struct B { void f(); }; int main() { int n = 1; int* pn = &n; // pointeur int* pn2 = &*pn; // pn2 == pn int A::* mp = &A::i; // pointeur vers membre de données void (B::*mpf)() = &B::f; // pointeur vers fonction membre void (*pf)(int) = &f; // résolution de surcharge due au contexte d'initialisation // auto pf2 = &f; // erreur : type de fonction surchargée ambigu auto pf2 = static_cast<void (*)(int)>(&f); // résolution de surcharge due au cast }
Opérateurs d'accès aux membres intégrés
Les expressions d'opérateur d'accès membre ont la forme
expr
.template
(optionnel)
id-expr
|
(1) | ||||||||
expr
->template
(optionnel)
id-expr
|
(2) | ||||||||
expr
.
pseudo-destructeur
|
(3) | ||||||||
expr
->
pseudo-destructeur
|
(4) | ||||||||
T*
.
id-expr
est un nom (formellement, une
expression d'identifiant
qui désigne) un membre de données ou une fonction membre de
T
ou d'une classe de base
B
non ambiguë et accessible de
T
(par ex.
E1.
E2
ou
E1
-
>
E2
), optionnellement
qualifié
(par ex.
E1.
B
::
E2
ou
E1
-
>
B
::
E2
), optionnellement en utilisant le
template
disambiguateur
(par ex.
E1.
template
E2
ou
E1
-
>
template
E2
).
Si un operator - > défini par l'utilisateur est appelé, operator - > est appelé à nouveau sur la valeur résultante, récursivement, jusqu'à ce qu'un operator - > soit atteint qui renvoie un pointeur brut. Après cela, la sémantique intégrée est appliquée à ce pointeur.
L'expression E1 - > E2 est exactement équivalente à ( * E1 ) . E2 pour les types natifs ; c'est pourquoi les règles suivantes ne concernent que E1. E2 .
Dans l'expression E1. E2 :
-
Si
E2
est de type référence
T&ouT&&(depuis C++11) , le résultat est une lvalue de typeTdésignant l'objet ou la fonction à laquelle la référence est liée. -
Sinon, étant donné le type de
E2
comme
T, le résultat est une lvalue de typeTdésignant ce membre de données statique.
-
Si
E2
est de type référence
T&ouT&&(depuis C++11) , le résultat est une lvalue de typeTdésignant l'objet ou la fonction à laquelle le membre référence correspondant de E1 est lié. - Sinon, si E1 est une lvalue, le résultat est une lvalue désignant ce membre de données non statique de E1 .
- Sinon (si E1 est une rvalue (jusqu'à C++17) xvalue (qui peut être matérialisée à partir d'une prvalue) (depuis C++17) ), le résultat est une rvalue (jusqu'à C++11) xvalue (depuis C++11) désignant ce membre de données non statique de E1 .
- Si E2 est une fonction membre statique , le résultat est une lvalue désignant cette fonction membre statique. Essentiellement, E1 est évalué et ignoré dans ce cas.
- Sinon ( E2 est une fonction membre non statique ), le résultat est une prvalue désignant cette fonction membre non statique de E1 .
T
, le résultat est
une rvalue
(jusqu'à C++11)
une prvalue
(depuis C++11)
de type
T
dont la valeur est celle de l'énumérateur.
~
suivi du
nom de type
ou du
spécificateur decltype
désignant le même type (moins les qualifications cv), optionnellement
qualifié
, le résultat est un type spécial de prvalue qui ne peut être utilisé que comme opérande gauche d'un opérateur d'appel de fonction, et à aucune autre fin
operator. ne peut pas être surchargé, et pour operator - > , dans la résolution de surcharge face aux opérateurs définis par l'utilisateur , l'opérateur intégré n'introduit aucune signature de fonction supplémentaire : l'opérateur intégré operator - > ne s'applique pas s'il existe un operator - > surchargé qui est une fonction viable .
#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; // mot-clé template nécessaire pour un membre template dépendant int* p = T().template ptr<U>(); p->~U(); // U est int, appelle le pseudo-destructeur de int 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 fonctionne également << a.f() << ' ' << a.sf() << ' ' // A::sf() fonctionne également // << &a.f << ' ' // erreur : mal formé si a.f n'est pas le // opérande gauche de operator() // << a.B << ' ' // erreur : type imbriqué non autorisé << a.RED << ' '; // énumérateur UPtrWrapper uPtrWrap{std::make_unique<std::string>("wrapped")}; assert(uPtrWrap->data() == uPtrWrap.operator->().operator->()->data()); }
Sortie :
1 2 11 4 1
Si E2 est un membre non statique et que le résultat de E1 est un objet dont le type n'est pas similaire au type de E1 , le comportement est indéfini :
struct A { int i; }; struct B { int j; }; struct D : A, B {}; void f() { D d; static_cast<B&>(d).j; // OK, l'expression d'objet désigne le sous-objet B de d reinterpret_cast<B&>(d).j; // comportement indéfini }
Opérateurs d'accès aux pointeurs vers membres intégrés
Les expressions d'opérateur d'accès aux membres via des pointeurs vers des membres ont la forme
lhs
.*
rhs
|
(1) | ||||||||
lhs
->*
rhs
|
(2) | ||||||||
T
.
T*
.
rhs
doit être une rvalue de type pointeur vers membre (
data
ou
function
) de
T
ou pointeur vers membre d'une classe de base
B
de
T
non ambiguë et accessible.
L'expression E1 - > * E2 est exactement équivalente à ( * E1 ) . * E2 pour les types natifs ; c'est pourquoi les règles suivantes ne concernent que E1. * E2 .
Dans l'expression E1. * E2 :
- si E1 est une lvalue, le résultat est une lvalue désignant ce membre de données,
- sinon (si E1 est une rvalue (jusqu'en C++17) xvalue (qui peut être matérialisée à partir d'une prvalue) (depuis C++17) ), le résultat est une rvalue (jusqu'en C++11) xvalue (depuis C++11) désignant ce membre de données ;
&
, le programme est mal formé
sauf si la fonction membre a le qualificateur cv
const
mais pas
volatile
(depuis C++20)
;
|
7)
si
E1
est une lvalue et
E2
pointe vers une fonction membre avec le qualificatif de référence
&&
, le programme est mal formé.
|
(depuis C++11) |
Dans la
résolution de surcharge pour les opérateurs définis par l'utilisateur
, pour chaque combinaison de types
D
,
B
,
R
, où le type de classe
B
est soit la même classe que
D
, soit une classe de base non ambiguë et accessible de
D
, et
R
est soit un type objet soit un type fonction, la signature de fonction suivante participe à la résolution de surcharge :
|
R
&
operator
-
>
*
(
D
*
, R B
::
*
)
;
|
||
où les deux opérandes peuvent être qualifiés cv, auquel cas la qualification cv du type de retour est l'union des qualifications cv des opérandes.
#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; // erreur : impossible de modifier via mutable std::cout << s.*pmi << '\n'; D d(7); // les pointeurs de base fonctionnent avec l'objet dérivé D* pd = &d; std::cout << (d.*pf)(7) << ' ' << (pd->*pf)(8) << '\n'; }
Sortie :
7 14 15
Bibliothèque standard
L'opérateur d'indice est surchargé par de nombreuses classes de conteneurs standards :
|
accède à un bit spécifique
(fonction membre publique de
std::bitset<N>
)
|
|
|
fournit un accès indexé au tableau géré
(fonction membre publique de
std::unique_ptr<T,Deleter>
)
|
|
|
accède au caractère spécifié
(fonction membre publique de
std::basic_string<CharT,Traits,Allocator>
)
|
|
|
accède à l'élément spécifié
(fonction membre publique de
std::array<T,N>
)
|
|
|
accède à l'élément spécifié
(fonction membre publique de
std::deque<T,Allocator>
)
|
|
|
accède à l'élément spécifié
(fonction membre publique de
std::vector<T,Allocator>
)
|
|
|
accède ou insère l'élément spécifié
(fonction membre publique de
std::map<Key,T,Compare,Allocator>
)
|
|
|
accède ou insère l'élément spécifié
(fonction membre publique de
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>
)
|
|
|
accède à un élément par index
(fonction membre publique de
std::reverse_iterator<Iter>
)
|
|
|
accède à un élément par index
(fonction membre publique de
std::move_iterator<Iter>
)
|
|
|
obtient/définit un élément, une tranche ou un masque de valarray
(fonction membre publique de
std::valarray<T>
)
|
|
|
retourne la sous-correspondance spécifiée
(fonction membre publique de
std::match_results<BidirIt,Alloc>
)
|
Les opérateurs d'indirection et de membre sont surchargés par de nombreux itérateurs et classes de pointeurs intelligents :
|
déréférence le pointeur vers l'objet géré
(fonction membre publique de
std::unique_ptr<T,Deleter>
)
|
|
|
déréférence le pointeur stocké
(fonction membre publique de
std::shared_ptr<T>
)
|
|
|
accède à l'objet géré
(fonction membre publique de
std::auto_ptr<T>
)
|
|
|
déréférence l'itérateur
(fonction membre publique de
std::raw_storage_iterator<OutputIt,T>
)
|
|
|
déréférence l'itérateur sous-jacent décrémenté
(fonction membre publique de
std::reverse_iterator<Iter>
)
|
|
|
opération nulle
(fonction membre publique de
std::back_insert_iterator<Container>
)
|
|
|
opération nulle
(fonction membre publique de
std::front_insert_iterator<Container>
)
|
|
|
opération nulle
(fonction membre publique de
std::insert_iterator<Container>
)
|
|
|
accède à l'élément pointé
(fonction membre publique de
std::move_iterator<Iter>
)
|
|
|
retourne l'élément courant
(fonction membre publique de
std::istream_iterator<T,CharT,Traits,Distance>
)
|
|
|
opération nulle
(fonction membre publique de
std::ostream_iterator<T,CharT,Traits>
)
|
|
|
obtient une copie du caractère courant
(fonction membre publique de
std::istreambuf_iterator<CharT,Traits>
)
|
|
|
opération nulle
(fonction membre publique de
std::ostreambuf_iterator<CharT,Traits>
)
|
|
|
accède à la correspondance courante
(fonction membre publique de
std::regex_iterator<BidirIt,CharT,Traits>
)
|
|
|
accède à la sous-correspondance courante
(fonction membre publique de
std::regex_token_iterator<BidirIt,CharT,Traits>
)
|
Aucune classe de la bibliothèque standard ne surcharge
operator
&
. L'exemple le plus connu de
operator
&
surchargé est la classe Microsoft COM
CComPtr
, bien qu'il puisse également apparaître dans des EDSL tels que
boost.spirit
.
Aucune classe de la bibliothèque standard ne surcharge operator - > * . Il a été suggéré que cela pourrait faire partie de l'interface des pointeurs intelligents , et en effet il est utilisé dans cette capacité par les acteurs dans boost.phoenix , mais est plus courant dans les EDSL tels que cpp.react .
Notes
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_multidimensional_subscript
|
202110L
|
(C++23) | Opérateur d'indice multidimensionnel |
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement correct |
|---|---|---|---|
| CWG 1213 | C++11 | l'indice d'un rvalue de tableau donnait un lvalue | reclassifié comme xvalue |
| CWG 1458 | C++98 |
l'application de
&
à un lvalue de type classe incomplet qui
déclare operator & résultait en un comportement indéfini |
il n'est pas spécifié
quel & est utilisé |
| CWG 1642 | C++98 | le rhs dans les opérateurs d'accès aux pointeurs de membres intégrés pouvait être un lvalue | ne peut être qu'un rvalue |
| CWG 1800 | C++98 |
lors de l'application de
&
à un membre de données non statique d'une
union anonyme membre, il n'était pas clair si l'union anonyme participait au type de résultat |
l'union anonyme
n'est pas incluse dans le type de résultat |
| CWG 2614 | C++98 | le résultat de E1. E2 n'était pas clair si E2 est un membre référence ou un énumérateur | clarifié |
| CWG 2725 | C++98 |
si
E2
est une fonction membre statique,
E1.
E2
est bien formé
même s'il n'est pas l'opérande gauche de operator ( ) |
E1.
E2
est mal formé
dans ce cas |
| CWG 2748 | C++98 |
le comportement de
E1
-
>
E2
n'était pas clair si
E1
est un
pointeur nul et E2 fait référence à un membre statique |
le comportement est
indéfini dans ce cas |
| CWG 2813 | C++98 |
E1
n'était pas une expression de valeur rejetée si
E1. E2 nomme un membre statique ou une énumération |
il l'est |
| CWG 2823 | C++98 |
le comportement de
*
expr
n'était pas clair si
expr
ne pointe pas vers un objet ou une fonction |
clarifié |
Voir aussi
| Opérateurs courants | ||||||
|---|---|---|---|---|---|---|
| affectation |
incrémentation
décrémentation |
arithmétique | logique | comparaison |
accès
membre |
autres |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
...
]
|
appel de fonction
a ( ... ) |
|
virgule
a, b |
||||||
|
conditionnel
a ? b : c |
||||||
| Opérateurs spéciaux | ||||||
|
static_cast
convertit un type en un autre type apparenté
|
||||||
|
Documentation C
pour
Opérateurs d'accès aux membres
|