class X { static void f(); void f(); // error void f() const; // error void f() const volatile; // error void g(); void g() const; // OK: no static g void g() const volatile; // OK: no static g };— end example
class Y { void h() &; void h() const &; // OK void h() &&; // OK, all declarations have a ref-qualifier void i() &; void i() const; // error: prior declaration of i has a ref-qualifier };— end example
typedef int Int; void f(int i); void f(Int i); // OK: redeclaration of f(int) void f(int i) { /* ... */ } void f(Int i) { /* ... */ } // error: redefinition of f(int)— end example
enum E { a }; void f(int i) { /* ... */ } void f(E i) { /* ... */ }— end example
int f(char*); int f(char[]); // same as f(char*); int f(char[7]); // same as f(char*); int f(char[9]); // same as f(char*); int g(char(*)[10]); int g(char[5][10]); // same as g(char(*)[10]); int g(char[7][10]); // same as g(char(*)[10]); int g(char(*)[20]); // different from g(char(*)[10]);— end example
void h(int()); void h(int (*)()); // redeclaration of h(int()) void h(int x()) { } // definition of h(int()) void h(int (*x)()) { } // error: redefinition of h(int())— end example
typedef const int cInt; int f (int); int f (const int); // redeclaration of f(int) int f (int) { /* ... */ } // definition of f(int) int f (cInt) { /* ... */ } // error: redefinition of f(int)— end example
void f (int i, int j); void f (int i, int j = 99); // OK: redeclaration of f(int, int) void f (int i = 88, int j); // OK: redeclaration of f(int, int) void f (); // OK: overloaded declaration of f void prog () { f (1, 2); // OK: call f(int, int) f (1); // OK: call f(int, int) f (); // error: f(int, int) or f()? }— end example
template<int I> concept C = true; template<typename T> struct A { void f() requires C<42>; // #1 void f() requires true; // OK, different functions };— end example
void f(const char*); void g() { extern void f(int); f("asdf"); // error: f(int) hides f(const char*) // so there is no f(const char*) in this scope } void caller () { extern void callee(int, int); { extern void callee(int); // hides callee(int, int) callee(88, 99); // error: only callee(int) in scope } }— end example
class T { public: T(); }; class C : T { public: C(int); }; T a = 1; // error: no viable conversion (T(C(1)) not considered)— end example
struct A { A(); // #1 A(A &&); // #2 template<typename T> A(T &&); // #3 }; struct B : A { using A::A; B(const B &); // #4 B(B &&) = default; // #5, implicitly deleted struct X { X(X &&) = delete; } x; }; extern B b1; B b2 = static_cast<B&&>(b1); // calls #4: #1 is not viable, #2, #3, and #5 are not candidates struct C { operator B&&(); }; B b3 = C(); // calls #4— end example
postfix-expression ( expression-list )if the postfix-expression names at least one function or function template, overload resolution is applied as specified in [over.call.func].
postfix-expression: postfix-expression . id-expression postfix-expression -> id-expression primary-expression
operator conversion-type-id ( ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seq ;where the optional cv-qualifier-seq is the same cv-qualification as, or a greater cv-qualification than, cv, and where conversion-type-id denotes the type “pointer to function of () returning R”, or the type “reference to pointer to function of () returning R”, or the type “reference to function of () returning R”, a surrogate call function with the unique name call-function and having the form
R call-function ( conversion-type-id F, P a, …, P a) { return F (a, …, a); }is also considered as a candidate function.
int f1(int); int f2(float); typedef int (*fp1)(int); typedef int (*fp2)(float); struct A { operator fp1() { return f1; } operator fp2() { return f2; } } a; int i = a(1); // calls f1 via pointer returned from conversion function— end example
struct String { String (const String&); String (const char*); operator const char* (); }; String operator + (const String&, const String&); void f() { const char* p= "one" + "two"; // error: cannot add two pointers; overloaded operator+ not considered // because neither operand has class or enumeration type int I = 1 + 1; // always evaluates to 2 even if class or enumeration types exist // that would perform the operation. }— end example
Subclause | Expression | As member function | As non-member function |
@a | |||
a@b | |||
a=b | |||
a[b] | |||
a-> | |||
a@ |
struct X { operator double(); }; struct Y { operator int*(); }; int *a = Y() + 100.0; // error: pointer arithmetic requires integral operand int *b = Y() + X(); // error: pointer arithmetic requires integral operand— end example
struct A { }; void operator + (A, A); struct B { void operator + (B); void f (); }; A a; void B::f() { operator+ (a,a); // error: global operator hidden by member a + a; // OK: calls global operator+ }— end note
typename nested-name-specifier template simple-template-idas specified in [dcl.type.simple].
template <typename> class AA;with a single partial specialization whose template parameter list is that of A and whose template argument list is a specialization of A with the template argument list of A ([temp.dep.type]), AA<T> matches the partial specialization.
template <class T> struct A { explicit A(const T&, ...) noexcept; // #1 A(T&&, ...); // #2 }; int i; A a1 = { i, i }; // error: explicit constructor #1 selected in copy-list-initialization during deduction, // cannot deduce from non-forwarding rvalue reference in #2 A a2{i, i}; // OK, #1 deduces to A<int> and also initializes A a3{0, i}; // OK, #2 deduces to A<int> and also initializes A a4 = {0, i}; // OK, #2 deduces to A<int> and also initializes template <class T> A(const T&, const T&) -> A<T&>; // #3 template <class T> explicit A(T&&, T&&) -> A<T>; // #4 A a5 = {0, 1}; // error: explicit deduction guide #4 selected in copy-list-initialization during deduction A a6{0,1}; // OK, #4 deduces to A<int> and #2 initializes A a7 = {0, i}; // error: #3 deduces to A<int&>, #1 and #2 declare same constructor A a8{0,i}; // error: #3 deduces to A<int&>, #1 and #2 declare same constructor template <class T> struct B { template <class U> using TA = T; template <class U> B(U, TA<U>); }; B b{(int*)0, (char*)0}; // OK, deduces B<char*> template <typename T> struct S { T x; T y; }; template <typename T> struct C { S<T> s; T t; }; template <typename T> struct D { S<int> s; T t; }; C c1 = {1, 2}; // error: deduction failed C c2 = {1, 2, 3}; // error: deduction failed C c3 = {{1u, 2u}, 3}; // OK, deduces C<int> D d1 = {1, 2}; // error: deduction failed D d2 = {1, 2, 3}; // OK, braces elided, deduces D<int> template <typename T> struct E { T t; decltype(t) t2; }; E e1 = {1, 2}; // OK, deduces E<int> template <typename... T> struct Types {}; template <typename... T> struct F : Types<T...>, T... {}; struct X {}; struct Y {}; struct Z {}; struct W { operator Y(); }; F f1 = {Types<X, Y, Z>{}, {}, {}}; // OK, F<X, Y, Z> deduced F f2 = {Types<X, Y, Z>{}, X{}, Y{}}; // OK, F<X, Y, Z> deduced F f3 = {Types<X, Y, Z>{}, X{}, W{}}; // error: conflicting types deduced; operator Y not considered— end example
template <class T, class U> struct C { C(T, U); // #1 }; template<class T, class U> C(T, U) -> C<T, std::type_identity_t<U>>; // #2 template<class V> using A = C<V *, V *>; template<std::integral W> using B = A<W>; int i{}; double d{}; A a1(&i, &i); // deduces A<int> A a2(i, i); // error: cannot deduce V * from i A a3(&i, &d); // error: #1: cannot deduce (V*, V*) from (int *, double *) // #2: cannot deduce A<V> from C<int *, double *> B b1(&i, &i); // deduces B<int> B b2(&d, &d); // error: cannot deduce B<W> from C<double *, double *>
// The following concept ensures a specialization of A is deduced. template <class> class AA; template <class V> class AA<A<V>> { }; template <class T> concept deduces_A = requires { sizeof(AA<T>); }; // f1 is formed from the constructor #1 of C, generating the following function template template<T, U> auto f1(T, U) -> C<T, U>; // Deducing arguments for C<T, U> from C<V *, V*> deduces T as V * and U as V *; // f1' is obtained by transforming f1 as described by the above procedure. template<class V> requires deduces_A<C<V *, V *>> auto f1_prime(V *, V*) -> C<V *, V *>; // f2 is formed from the deduction-guide #2 of C template<class T, class U> auto f2(T, U) -> C<T, std::type_identity_t<U>>; // Deducing arguments for C<T, std::type_identity_t<U>> from C<V *, V*> deduces T as V *; // f2' is obtained by transforming f2 as described by the above procedure. template<class V, class U> requires deduces_A<C<V *, std::type_identity_t<U>>> auto f2_prime(V *, U) -> C<V *, std::type_identity_t<U>>; // The following concept ensures a specialization of B is deduced. template <class> class BB; template <class V> class BB<B<V>> { }; template <class T> concept deduces_B = requires { sizeof(BB<T>); }; // The guides for B derived from the above f1' and f2' for A are as follows: template<std::integral W> requires deduces_A<C<W *, W *>> && deduces_B<C<W *, W *>> auto f1_prime_for_B(W *, W *) -> C<W *, W *>; template<std::integral W, class U> requires deduces_A<C<W *, std::type_identity_t<U>>> && deduces_B<C<W *, std::type_identity_t<U>>> auto f2_prime_for_B(W *, U) -> C<W *, std::type_identity_t<U>>;
struct A { A(); operator int(); operator double(); } a; int i = a; // a.operator int() followed by no conversion is better than // a.operator double() followed by a conversion to int float x = a; // ambiguous: both possibilities require conversions, // and neither is better than the other— end example
template <class T> struct A { operator T&(); // #1 operator T&&(); // #2 }; typedef int Fn(); A<Fn> a; Fn& lf = a; // calls #1 Fn&& rf = a; // calls #2— end example
struct A { A(int = 0); }; struct B: A { using A::A; B(); }; int main() { B b; // OK, B::B() }— end example
struct S { friend auto operator<=>(const S&, const S&) = default; // #1 friend bool operator<(const S&, const S&); // #2 }; bool b = S() < S(); // calls #2— end example
struct S { friend std::weak_ordering operator<=>(const S&, int); // #1 friend std::weak_ordering operator<=>(int, const S&); // #2 }; bool b = 1 < S(); // calls #2— end example
template <class T> struct A { using value_type = T; A(value_type); // #1 A(const A&); // #2 A(T, T, int); // #3 template<class U> A(int, T, U); // #4 // #5 is the copy deduction candidate, A(A) }; A x(1, 2, 3); // uses #3, generated from a non-template constructor template <class T> A(T) -> A<T>; // #6, less specialized than #5 A a(42); // uses #6 to deduce A<int> and #1 to initialize A b = a; // uses #5 to deduce A<int> and #2 to initialize template <class T> A(A<T>) -> A<A<T>>; // #7, as specialized as #5 A b2 = a; // uses #7 to deduce A<A<int>> and #1 to initialize— end example
void Fcn(const int*, short); void Fcn(int*, int); int i; short s = 0; void f() { Fcn(&i, s); // is ambiguous because &i → int* is better than &i → const int* // but s → short is also better than s → int Fcn(&i, 1L); // calls Fcn(int*, int), because &i → int* is better than &i → const int* // and 1L → short and 1L → int are indistinguishable Fcn(&i, 'c'); // calls Fcn(int*, int), because &i → int* is better than &i → const int* // and c → int is better than c → short }— end example
namespace A { extern "C" void f(int = 5); } namespace B { extern "C" void f(int = 5); } using A::f; using B::f; void use() { f(3); // OK, default argument was not used for viability f(); // error: found default argument twice }— end example
class B; class A { A (B&);}; class B { operator A (); }; class C { C (B&); }; void f(A) { } void f(C) { } B b; f(b); // error: ambiguous because there is a conversion b → C (via constructor) // and an (ambiguous) conversion b → A (via constructor or conversion function) void f(B) { } f(b); // OK, unambiguous— end example
Conversion | Category | Rank | Subclause |
No conversions required | Identity | ||
Lvalue-to-rvalue conversion | |||
Array-to-pointer conversion | Lvalue Transformation | ||
Function-to-pointer conversion | Exact Match | ||
Qualification conversions | |||
Function pointer conversion | Qualification Adjustment | ||
Integral promotions | |||
Floating-point promotion | Promotion | Promotion | |
Integral conversions | |||
Floating-point conversions | |||
Floating-integral conversions | |||
Pointer conversions | Conversion | Conversion | |
Pointer-to-member conversions | |||
Boolean conversions |
struct A {}; struct B : public A {} b; int f(A&); int f(B&); int i = f(b); // calls f(B&), an exact match, rather than f(A&), a conversion— end example
struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order ([dcl.init.list]) g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }— end example
void f(std::initializer_list<int>); f( {} ); // OK: f(initializer_list<int>) identity conversion f( {1,2,3} ); // OK: f(initializer_list<int>) identity conversion f( {'a','b'} ); // OK: f(initializer_list<int>) integral promotion f( {1.0} ); // error: narrowing struct A { A(std::initializer_list<double>); // #1 A(std::initializer_list<complex<double>>); // #2 A(std::initializer_list<std::string>); // #3 }; A a{ 1.0,2.0 }; // OK, uses #1 void g(A); g({ "foo", "bar" }); // OK, uses #3 typedef int IA[3]; void h(const IA&); h({ 1, 2, 3 }); // OK: identity conversion— end example
struct A { A(std::initializer_list<int>); }; void f(A); f( {'a', 'b'} ); // OK: f(A(std::initializer_list<int>)) user-defined conversion struct B { B(int, double); }; void g(B); g( {'a', 'b'} ); // OK: g(B(int, double)) user-defined conversion g( {1.0, 1.0} ); // error: narrowing void f(B); f( {'a', 'b'} ); // error: ambiguous f(A) or f(B) struct C { C(std::string); }; void h(C); h({"foo"}); // OK: h(C(std::string("foo"))) struct D { D(A, C); }; void i(D); i({ {1,2}, {"bar"} }); // OK: i(D(A(std::initializer_list<int>{1,2}), C(std::string("bar"))))— end example
struct A { int m1; double m2; }; void f(A); f( {'a', 'b'} ); // OK: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing— end example
struct A { int m1; double m2; }; void f(const A&); f( {'a', 'b'} ); // OK: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing void g(const double &); g({1}); // same conversion as int to double— end example
void f(int); f( {'a'} ); // OK: same conversion as char to int f( {1.0} ); // error: narrowing— end example
void f(int); f( { } ); // OK: identity conversion— end example
void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // chooses #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo","bar"}); } // chooses #4— end example
void f(int (&&)[] ); // #1 void f(double (&&)[] ); // #2 void f(int (&&)[2]); // #3 f( {1} ); // Calls #1: Better than #2 due to conversion, better than #3 due to bounds f( {1.0} ); // Calls #2: Identity conversion is better than floating-integral conversion f( {1.0, 2.0} ); // Calls #2: Identity conversion is better than floating-integral conversion f( {1, 2} ); // Calls #3: Converting to array of known bound is better than to unknown bound, // and an identity conversion is better than floating-integral conversion— end example
int i; int f1(); int&& f2(); int g(const int&); int g(const int&&); int j = g(i); // calls g(const int&) int k = g(f1()); // calls g(const int&&) int l = g(f2()); // calls g(const int&&) struct A { A& operator<<(int); void p() &; void p() &&; }; A& operator<<(A&&, char); A() << 1; // calls A::operator<<(int) A() << 'c'; // calls operator<<(A&&, char) A a; a << 1; // calls A::operator<<(int) a << 'c'; // calls A::operator<<(int) A().p(); // calls A::p()&& a.p(); // calls A::p()&— end example
int f(void(&)()); // #1 int f(void(&&)()); // #2 void g(); int i1 = f(g); // calls #1— end example
int f(const volatile int *); int f(const int *); int i; int j = f(&i); // calls f(const int*)— end example
int f(const int &); int f(int &); int g(const int &); int g(int); int i; int j = f(i); // calls f(int &) int k = g(i); // ambiguous struct X { void f() const; void f(); }; void g(const X& a, X b) { a.f(); // calls X::f() const b.f(); // calls X::f() }— end example
struct A { operator short(); } a; int f(int); int f(float); int i = f(a); // calls f(int), because short → int is // better than short → float.— end example
struct A {}; struct B : public A {}; struct C : public B {}; C* pc; int f(A*); int f(B*); int i = f(pc); // calls f(B*)— end example
int f(double); int f(int); int (*pfd)(double) = &f; // selects f(double) int (*pfi)(int) = &f; // selects f(int) int (*pfe)(...) = &f; // error: type mismatch int (&rfi)(int) = f; // selects f(int) int (&rfd)(double) = f; // selects f(double) void g() { (int (*)(int))&f; // cast expression as selector }
struct X { int f(int); static int f(long); }; int (X::*p1)(int) = &X::f; // OK int (*p2)(int) = &X::f; // error: mismatch int (*p3)(long) = &X::f; // OK int (X::*p4)(long) = &X::f; // error: mismatch int (X::*p5)(int) = &(X::f); // error: wrong syntax for // pointer to member int (*p6)(long) = &(X::f); // OK— end example
operator-function-id: operator operator
operator: one of new delete new[] delete[] co_await ( ) [ ] -> ->* ~ ! + - * / % ^ & | = += -= *= /= %= ^= &= |= == != < > <= >= <=> && || << >> <<= >>= ++ -- ,
. .* :: ?:
nor can the preprocessing symbols
# ([cpp.stringize])
and
## ([cpp.concat]).complex z = a.operator+(b); // complex z = a+b; void* p = operator new(sizeof(int)*n);— end example
cast-expression . operator @ ()Otherwise, if a non-member function is selected, the expression is interpreted as
operator @ ( cast-expression )
struct B { virtual int operator= (int); virtual B& operator= (const B&); }; struct D : B { virtual int operator= (int); virtual D& operator= (const B&); }; D dobj1; D dobj2; B* bptr = &dobj1; void f() { bptr->operator=(99); // calls D::operator=(int) *bptr = 99; // ditto bptr->operator=(dobj2); // calls D::operator=(const B&) *bptr = dobj2; // ditto dobj1 = dobj2; // calls implicitly-declared D::operator=(const D&) }— end example
postfix-expression ( expression-list )where the postfix-expression is of class type, the operator function is selected by overload resolution ([over.call.object]).
postfix-expression . operator conversion-type-id () ( expression-list )Otherwise, the expression is interpreted as
postfix-expression . operator () ( expression-list )
postfix-expression [ expr-or-braced-init-list ]the operator function is selected by overload resolution ([over.match.oper]).
postfix-expression . operator [] ( expr-or-braced-init-list )
postfix-expression -> template id-expressionthe operator function is selected by overload resolution ([over.match.oper]), and the expression is interpreted as
( postfix-expression . operator -> () ) -> template id-expression
struct X { X& operator++(); // prefix ++a X operator++(int); // postfix a++ }; struct Y { }; Y& operator++(Y&); // prefix ++b Y operator++(Y&, int); // postfix b++ void f(X a, Y b) { ++a; // a.operator++(); a++; // a.operator++(0); ++b; // operator++(b); b++; // operator++(b, 0); a.operator++(); // explicit call: like ++a; a.operator++(0); // explicit call: like a++; operator++(b); // explicit call: like ++b; operator++(b, 0); // explicit call: like b++; }— end example
vq T& operator++(vq T&); T operator++(vq T&, int);
vq T& operator--(vq T&); T operator--(vq T&, int);
T*vq& operator++(T*vq&); T*vq& operator--(T*vq&); T* operator++(T*vq&, int); T* operator--(T*vq&, int);
T& operator*(T*);
T& operator*(T*);
T operator+(T); T operator-(T);
T operator~(T);
cv12 T& operator->*(cv1 C1*, cv2 T C2::*);where cv12 is the union of cv1 and cv2.
LR operator*(L, R); LR operator/(L, R); LR operator+(L, R); LR operator-(L, R); bool operator==(L, R); bool operator!=(L, R); bool operator<(L, R); bool operator>(L, R); bool operator<=(L, R); bool operator>=(L, R);where LR is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L and R.
std::strong_ordering operator<=>(T, T);
std::partial_ordering operator<=>(L, R);
T* operator+(T*, std::ptrdiff_t); T& operator[](T*, std::ptrdiff_t); T* operator-(T*, std::ptrdiff_t); T* operator+(std::ptrdiff_t, T*); T& operator[](std::ptrdiff_t, T*);
std::ptrdiff_t operator-(T, T);
bool operator==(T, T); bool operator!=(T, T); bool operator<(T, T); bool operator>(T, T); bool operator<=(T, T); bool operator>=(T, T); R operator<=>(T, T);where R is the result type specified in [expr.spaceship].
bool operator==(T, T); bool operator!=(T, T);
LR operator%(L, R); LR operator&(L, R); LR operator^(L, R); LR operator|(L, R); L operator<<(L, R); L operator>>(L, R);where LR is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L and R.
vq L& operator=(vq L&, R); vq L& operator*=(vq L&, R); vq L& operator/=(vq L&, R); vq L& operator+=(vq L&, R); vq L& operator-=(vq L&, R);
T*vq& operator=(T*vq&, T*);
vq T& operator=(vq T&, T);
T*vq& operator+=(T*vq&, std::ptrdiff_t); T*vq& operator-=(T*vq&, std::ptrdiff_t);
vq L& operator%=(vq L&, R); vq L& operator<<=(vq L&, R); vq L& operator>>=(vq L&, R); vq L& operator&=(vq L&, R); vq L& operator^=(vq L&, R); vq L& operator|=(vq L&, R);
bool operator!(bool); bool operator&&(bool, bool); bool operator||(bool, bool);
LR operator?:(bool, L, R);where LR is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L and R.
literal-operator-id: operator string-literal identifier operator user-defined-string-literal
const char* unsigned long long int long double char wchar_t char8_t char16_t char32_t const char*, std::size_t const wchar_t*, std::size_t const char8_t*, std::size_t const char16_t*, std::size_t const char32_t*, std::size_t
void operator "" _km(long double); // OK string operator "" _i18n(const char*, std::size_t); // OK template <char...> double operator "" _\u03C0(); // OK: UCN for lowercase pi float operator ""_e(const char*); // OK float operator ""E(const char*); // error: reserved literal suffix ([usrlit.suffix], [lex.ext]) double operator""_Bq(long double); // OK: does not use the reserved identifier _Bq ([lex.name]) double operator"" _Bq(long double); // uses the reserved identifier _Bq ([lex.name]) float operator " " B(const char*); // error: non-empty string-literal string operator "" 5X(const char*, std::size_t); // error: invalid literal suffix identifier double operator "" _miles(double); // error: invalid parameter-declaration-clause template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause extern "C" void operator "" _m(long double); // error: C language linkage— end example