/ | ||||
(C++11) | ||||
(C++11) |
(C++11) | ||||
(C++20) | ||||
(C++20) |
(C++11) | ||||
expression |
pointer |
specifier | ||||
specifier (C++11) | ||||
specifier (C++11) |
(C++11) | ||||
(C++11) |
(C++11) | ||||
(C++11) |
Declarations | ||||
specifier | ||||
(C++11) | ||||
(C++20) | ||||
Function calls | ||||
Overloading | ||||
In order to compile a function call, the compiler must first perform name lookup , which, for functions, may involve argument-dependent lookup , and for function templates may be followed by template argument deduction .
If the name refers to more than one entity, it is said to be overloaded , and the compiler must determine which overload to call. In simple terms, the overload whose parameters match the arguments most closely is the one that is called.
In detail, overload resolution proceeds through the following steps:
Besides function calls, overloaded function names may appear in several additional contexts, where different rules apply: see Address of an overloaded function .
If a function cannot be selected by overload resolution, it cannot be used (e.g. it is a templated entity with a failed constraint ).
Candidate functions Call to a named function Call to a class object Call to an overloaded operator Initialization by constructor Copy-initialization by conversion Non-class initialization by conversion Reference initialization by conversion List-initialization Additional rules for function template candidates Additional rules for constructor candidates Additional rules for member function candidates Viable functions Best viable function Ranking of implicit conversion sequences Implicit conversion sequence in list-initialization Defect reports References See also |
Before overload resolution begins, the functions selected by name lookup and template argument deduction are combined to form the set of candidate functions . The exact details depend on the context in which overload resolution will take place.
If E in a function call expression E ( args ) names a set of overloaded functions and/or function templates (but not callable objects), the following rules are followed:
If E in a function call expression E ( args ) has class type cv T , then
In any case, the argument list for the purpose of overload resolution is the argument list of the function call expression preceded by the implied object argument E (when matching against the surrogate function, the user-defined conversion will automatically convert the implied object argument to the first argument of the surrogate function).
If at least one of the arguments to an operator in an expression has a class type or an enumeration type, both builtin operators and user-defined operator overloads participate in overload resolution, with the set of candidate functions selected as follows:
For a unary operator @ whose argument has type T1 (after removing cv-qualifications), or binary operator @ whose left operand has type T1 and right operand of type T2 (after removing cv-qualifications), the following sets of candidate functions are prepared:
: y, x <= y, x > y, and x >= y, all member, non-member, and built-in operator<=>s found are added to the set. y, x <= y, x > y, and x >= y as well as the three-way comparison expression x <=> y, a synthesized candidate with the order of the two parameters reversed is added for each member, non-member, and built-in operator<=>s found. = y, all member, non-member, and built-in operator==s found are added to the set, unless there is a matching operator!=. y and x != y, a synthesized candidate with the order of the two parameters reversed is added for each member, non-member, and built-in operator==s found, unless there is a matching operator!=. In all cases, rewritten candidates are not considered in the context of the rewritten expression. For all other operators, the rewritten candidate set is empty. | (since C++20) |
The set of candidate functions to be submitted for overload resolution is a union of the sets above. The argument list for the purpose of overload resolution consists of the operands of the operator except for operator-> , where the second operand is not an argument for the function call (see member access operator ).
If the overload resolution selects a built-in candidate, the user-defined conversion sequence from an operand of class type is not allowed to have a second standard conversion sequence: the user-defined conversion function must give the expected operand type directly:
For operator, , the unary operator & , and operator - > , if there are no viable functions (see below) in the set of candidate functions, then the operator is reinterpreted as a built-in.
If a rewritten operator<=> candidate is selected by overload resolution for an operator , x @ y is interpreted as the rewritten expression: 0 @ (y <=> x) if the selected candidate is a synthesized candidate with reversed order of parameters, or (x <=> y) @ 0 otherwise, using the selected rewritten operator<=> candidate. If a rewritten operator== candidate is selected by overload resolution for an operator (which is either or ), its return type must be (possibly cv-qualified) bool, and x @ y is interpreted as the rewritten expression: y == x or !(y == x) if the selected candidate is a synthesized candidate with reversed order of parameters, or !(x == y) otherwise, using the selected rewritten operator== candidate. Overload resolution in this case has a final tiebreaker preferring non-rewritten candidates to rewritten candidates, and preferring non-synthesized rewritten candidates to synthesized rewritten candidates. This lookup with the reversed arguments order makes it possible to write just operator<=>( , const char*) and operator==( , const char*) to generate all comparisons between and const char*, both ways. See for more detail. | (since C++20) |
When an object of class type is direct-initialized or default-initialized (including default-initialization in the context of copy-list-initialization ) (since C++11) , the candidate functions are all constructors of the class being initialized. The argument list is the expression list of the initializer.
Otherwise, the candidate functions are all converting constructors of the class being initialized. The argument list is the expression of the initializer.
For default-initialization in the context of copy-list-initialization, if an constructor is chosen, the initialization is ill-formed. | (since C++11) |
If copy-initialization of an object of class type requires that a user-defined conversion is called to convert the initializer expression of type cv S to the type cv T of the object being initialized, the following functions are candidate functions:
Either way, the argument list for the purpose of overload resolution consists of a single argument which is the initializer expression, which will be compared against the first argument of the constructor or against the implicit object argument of the conversion function.
When initialization of an object of non-class type cv1 T requires a user-defined conversion function to convert from an initializer expression of class type cv S , the following functions are candidates:
Either way, the argument list for the purpose of overload resolution consists of a single argument which is the initializer expression, which will be compared against the implicit object argument of the conversion function.
During reference initialization , where the reference to cv1 T is bound to the lvalue or rvalue result of a conversion from the initializer expression from the class type cv2 S , the following functions are selected for the candidate set:
When an object of non-aggregate class type T is list-initialized , two-phase overload resolution takes place.
If the initializer list is empty and T has a default constructor, phase 1 is skipped.
In copy-list-initialization, if phase 2 selects an explicit constructor, the initialization is ill-formed (as opposed to all over copy-initializations where explicit constructors are not even considered).
If name lookup found a function template, template argument deduction and checking of any explicit template arguments are performed to find the template argument values (if any) that can be used in this case:
If a name refers to one or more function templates and also to a set of overloaded non-template functions, those functions and the specializations generated from the templates are all candidates.
See function template overloading for further detail.
If a constructor template or conversion function template has a which happens to be , after deduction, if the context requires a candidate that is not explicit and the generated specialization is explicit, it is removed from the candidate set. | (since C++20) |
Defaulted and that are defined as deleted are excluded in the set of candidate functions. A constructor from class type that has a first parameter of type “reference to ” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type if all following conditions are satisfied: is to . is reference-related to . | (since C++11) |
If any candidate function is a member function (static or non-static) that does not have an explicit object parameter (since C++23) , but not a constructor, it is treated as if it has an extra parameter ( implicit object parameter ) which represents the object for which they are called and appears before the first of the actual parameters.
Similarly, the object on which a member function is being called is prepended to the argument list as the implied object argument .
For member functions of class X , the type of the implicit object parameter is affected by cv-qualifications and ref-qualifications of the member function as described in member functions .
The user-defined conversion functions are considered to be members of the implied object argument for the purpose of determining the type of the implicit object parameter .
The member functions introduced by a using-declaration into a derived class are considered to be members of the derived class for the purpose of defining the type of the implicit object parameter .
For the static member functions, the is considered to match any object: its type is not examined and no conversion sequence is attempted for it. | (until C++23) |
For the rest of overload resolution, the implied object argument is indistinguishable from other arguments, but the following special rules apply to the implicit object parameter :
Given the set of candidate functions, constructed as described above, the next step of overload resolution is examining arguments and parameters to reduce the set to the set of viable functions
To be included in the set of viable functions, the candidate function must satisfy the following:
If the function has an associated , it must be satisfied | (since C++20) |
User-defined conversions (both converting constructors and user-defined conversion functions) are prohibited from taking part in implicit conversion sequence where it would make it possible to apply more than one user-defined conversion. Specifically, they are not considered if the target of the conversion is the first parameter of a constructor or the implicit object parameter of a user-defined conversion function, and that constructor/user-defined conversion is a candidate for
, and the conversion is to or reference to (possibly cv-qualified) : A { A(int); }; struct B { B(A); }; B b{{0}}; // list-initialization of B // candidates: B(const B&), B(B&&), B(A) // {0} -> B&& not viable: would have to call B(A) // {0} -> const B&: not viable: would have to bind to rvalue, would have to call B(A) // {0} -> A viable. Calls A(int): user-defined conversion to A is not banned | (since C++11) |
For each pair of viable function F1 and F2 , the implicit conversion sequences from the i -th argument to i -th parameter are ranked to determine which one is better (except the first argument, the implicit object argument for static member functions has no effect on the ranking)
F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and
(only in context of initialization by conversion function for direct reference binding of a reference to function type), the result of is the same kind of reference (lvalue or rvalue) as the reference being initialized, and the result of is not, or, if not that, | (since C++11) |
and are non-template functions satisfying all following conditions: (since C++23). is more constrained than according to the . <typename T = int> struct S { constexpr void f(); // #1 constexpr void f(this S&) requires true; // #2 }; void test() { S<> s; s.f(); // calls #2 } , or, if not that, | (since C++20) |
is a constructor for a class D, is a constructor for a base class B of D, and for all arguments the corresponding parameters of and have the same type: A { A(int = 0); }; struct B: A { using A::A; B(); }; B b; // OK, B::B() , or, if not that, | (since C++11) |
is a rewritten candidate and is not, or, if not that, and are both rewritten candidates, and is a synthesized rewritten candidate with reversed order of parameters and is not, or, if not that, | (since C++20) |
is generated from a and is not, or, if not that, is the and is not, or, if not that, is generated from a non-template constructor and is generated from a constructor 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 A(A), the copy deduction candidate 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 | (since C++17) |
These pair-wise comparisons are applied to all viable functions. If exactly one viable function is better than all others, overload resolution succeeds and this function is called. Otherwise, compilation fails.
If the best viable function resolves to a function for which multiple declarations were found, and if any two of these declarations inhabit different scopes and specify a default argument that made the function viable, the program is ill-formed.
The argument-parameter implicit conversion sequences considered by overload resolution correspond to implicit conversions used in copy initialization (for non-reference parameters), except that when considering conversion to the implicit object parameter or to the left-hand side of assignment operator, conversions that create temporary objects are not considered. When the parameter is the implicit object parameter of a static member function, the implicit conversion sequence is a standard conversion sequence that is neither better nor worse than any other standard conversion sequence. (since C++23)
Each type of standard conversion sequence is assigned one of three ranks:
The rank of the standard conversion sequence is the worst of the ranks of the standard conversions it holds (there may be up to three conversions )
Binding of a reference parameter directly to the argument expression is either identity or a derived-to-base conversion:
Since ranking of conversion sequences operates with types and value categories only, a bit field can bind to a reference argument for the purpose of ranking, but if that function gets selected, it will be ill-formed.
the cv-qualification of the result of is a proper subset of the cv-qualification of the result of , and is not the (until C++11). | (until C++20) |
the result of can be converted to the result of by a qualification conversion. | (since C++20) |
A list-initialization sequence is than list-initialization sequence if the corresponding parameters are references to arrays, and L1 converts to type "array of N1 T," L2 converts to type "array of N2 T", and N1 is smaller than N2. | (since C++11) (until C++20) |
A list-initialization sequence is than list-initialization sequence if the corresponding parameters are references to arrays, and L1 and L2 convert to arrays of same element type, and either f(int (&&)[] ); // overload #1 void f(double (&&)[] ); // overload #2 void f(int (&&)[2]); // overload #3 f({1}); // #1: Better than #2 due to conversion, better than #3 due to bounds f({1.0}); // #2: double -> double is better than double -> int f({1.0, 2.0}); // #2: double -> double is better than double -> int f({1, 2}); // #3: -> int[2] is better than -> int[], // and int -> int is better than int -> double | (since C++20) |
If two conversion sequences are indistinguishable because they have the same rank, the following additional rules apply:
Conversion that promotes an whose underlying type is fixed to its underlying type is better than one that promotes to the promoted underlying type, if the two types are different. num : char { one = '0' }; << num::one; // '0', not 48 | (since C++11) |
A conversion in either direction between floating-point type and floating-point type is better than a conversion in the same direction between and arithmetic type if of is equal to the rank of , and is not a floating-point type, or is a floating-point type whose rank is not equal to the rank of , or of is greater than the subrank of . f( ); int f( ); int f(long long); float x; y; int i = f(x); // calls f(std::float32_t) on implementations where // float and std::float32_t have equal conversion ranks int j = f(y); // error: ambiguous, no equal conversion rank | (since C++23) |
Ambiguous conversion sequences are ranked as user-defined conversion sequences because multiple conversion sequences for an argument can exist only if they involve different user-defined conversions:
In list initialization , the argument is a braced-init-list , which isn't an expression, so the implicit conversion sequence into the parameter type for the purpose of overload resolution is decided by the following special rules:
is the one used. | (since C++20) |
If multiple constructors are viable but none is better than the others, the implicit conversion sequence is the ambiguous conversion sequence.
If the argument is a designated initializer list and the parameter is not a reference, a conversion is only possible if the parameter has an aggregate type that can be initialized from that initializer list according to the rules for , in which case the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion. If, after overload resolution, the order of declaration of the aggregate's members does not match for the selected overload, the initialization of the parameter will be ill-formed. 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 g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }
| (since C++20) |
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
C++98 | the behavior was unspecified when the same function with possibly different default arguments (from different scopes) is selected | the program is ill- formed in this case | |
C++98 | the conversion sequence from a string literal to char* was better than that to const char* even though the former is deprecated | the rank of the deprecated conversion is lowered (it was removed in C++11) | |
C++98 | it was invalid if the overload set named by contains a non-static member function in the case of | only invalid if overload resolution selects a non-static member function in this case | |
C++98 | references and pointers were handled insonsistently in overloading resolution with user defined conversions | they are handled consistently | |
C++98 | surrogate call functions were not added to the set of candidate functions for conversion functions declared in inaccessible base classes | removed the accessibility constraint, the program is ill-formed if a surrogate call function is selected and the corresponding conversion function cannot be called | |
C++98 | when a function template is selected as a candidate, its specializations were instantiated using template argument deduction | no instantiation will occur in this case, their declarations will be synthesized | |
C++98 | when the implicit conversions for arguments are equally good, a non-template conversion function was always better than a conversion function template, even if the latter may have a better standard conversion sequence | standard conversion sequences are compared before specialization levels | |
C++11 | overload resolution based on size of arrays was not specified | a shorter array is better when possible | |
C++11 | the determination of the candidate functions when binding a reference to a conversion result was not clear | made clear | |
C++98 | qualification conversion was checked before reference binding when comparing standard conversion sequences | reversed | |
C++11 | a non-explicit user-defined conversion function declared with a ref-qualifier did not have a corresponding surrogate function | it has a corresponding surrogate function | |
C++11 | same-type list-initialization of aggregates and arrays was omitted | initialization defined | |
C++11 | conversion from enum to its underlying type did not prefer the fixed underlying type | fixed type is preferred to what it promotes to | |
C++98 | the set of member candidates of a unary operator whose argument has type was empty if is a class currently being defined | the set is the result of qualified name lookup of in this case | |
C++98 | when a built-in candidate is selected by overload resolution, the operands would undergo conversion without restriction | only convert class type operands, and disabled the second standard conversion sequence | |
C++98 | ill-formed synthesized function template specializations could be added to the candidate set, making the program ill-formed | they are not added to the candidate set | |
C++11 | user-defined conversion is applied to the single initializer in a nested initializer list during list-initialization due to the resolution of | not applied | |
C++11 | initializer list constructors lost to copy constructors when list-initializing from {X} | non-aggregates consider initializer lists first | |
C++11 | there was no tiebreaker between inherited and non-inherited constructors | non-inherited constructor wins | |
C++20 | built-in candidates with the same parameter list as a rewritten non-member candidate were added to the list of built-in candidates | not added | |
C++98 | when a built-in assignment operator is considered, the first parameter could not bound to a temporary, which is already impossible | removed the redundant requirement | |
C++20 | the conversion restriction regarding designated initializer lists was applied even if the parameter is a reference | not restricted in this case | |
C++23 | the explicit object paramaeter was included when comparing parameter-type-lists | excluded | |
C++11 | the overload resolution for default-initialization in the context of copy-list-initialization only considered converting constructor | considers all constructors | |
C++20 | rewritten candidates based on operator== are added for a != b even if a matching operator!= exists | not added |
(C++20) | ||||
(C++20) | ||||
(C++11) | ||||
(C++11) | ||||
(C++11) | ||||
(C++17) | ||||
General topics | ||||
(C++11) |
- |
-expression |
- block |
(C++11) | ||||
(C++11) | ||||
(C++11) |
/ | ||||
(C++11) |
(C++11) | ||||
Expressions | ||||
expression |
pointer |
specifier |
(C++11) | ||||
(C++11) |
(C++11) | ||||
(C++11) |
(C++11) | ||||
(C++11) |
General | ||||
(lvalue, rvalue, xvalue) | ||||
(sequence points) | ||||
(C++11) | ||||
Literals | ||||
including | ||||
(C++11) | ||||
(C++11) | ||||
Operators | ||||
: , , , , , , , , , , | ||||
: , , , | ||||
: , , , , , , , , , , , , | ||||
: , , | ||||
: , , , , , , (C++20) | ||||
: , , , , , , | ||||
: , , | ||||
(C++20) | ||||
(C++17) | ||||
(C++11) | ||||
(C++11) | ||||
Conversions | ||||
, | ||||
Customizes the C++ operators for operands of user-defined types.
Overloaded operators are functions with special function names:
op | (1) | ||||||||
type | (2) | ||||||||
| (3) | ||||||||
| (4) | ||||||||
suffix-identifier | (5) | (since C++11) | |||||||
op | - | any of the following 38 (until C++20)40 (since C++20) operators:+ - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= <=> (since C++20) && || ++ -- , ->* -> ( ) [ ] co_await (since C++20) |
When an operator appears in an expression , and at least one of its operands has a class type or an enumeration type , then overload resolution is used to determine the user-defined function to be called among all the functions whose signatures match the following:
Expression | As member function | As non-member function | Example |
---|---|---|---|
@a | (a).operator@ ( ) | operator@ (a) | ! calls .operator!() |
a@b | (a).operator@ (b) | operator@ (a, b) | << 42 calls .operator<<(42) |
a=b | (a).operator= (b) | cannot be non-member | s; s = "abc"; calls s.operator=("abc") |
a(b...) | (a).operator()(b...) | cannot be non-member | r; auto n = r(); calls r.operator()() |
a[b] | (a).operator[](b) | cannot be non-member | <int, int> m; m[1] = 2; calls m.operator[](1) |
a-> | (a).operator-> ( ) | cannot be non-member | auto p = <S>(); p->bar() calls p.operator->() |
a@ | (a).operator@ (0) | operator@ (a, 0) | <int>::iterator i = v.begin(); i++ calls i.operator++(0) |
in this table, is a placeholder representing all matching operators: all prefix operators in @a, all postfix operators other than -> in a@, all infix operators other than = in a@b |
Note: for overloading user-defined conversion functions , user-defined literals , allocation and deallocation see their respective articles.
Overloaded operators (but not the built-in operators) can be called using function notation:
, , and (comma) lose their special when overloaded and behave like regular function calls even when they are used without function-call notation. | (until C++17) |
Other than the restrictions above, the language puts no other constraints on what the overloaded operators do, or on the return type (it does not participate in overload resolution), but in general, overloaded operators are expected to behave as similar as possible to the built-in operators: operator + is expected to add, rather than multiply its arguments, operator = is expected to assign, etc. The related operators are expected to behave similarly ( operator + and operator + = do the same addition-like operation). The return types are limited by the expressions in which the operator is expected to be used: for example, assignment operators return by reference to make it possible to write a = b = c = d , because the built-in operators allow that.
Commonly overloaded operators have the following typical, canonical forms: [1]
The assignment operator ( operator = ) has special properties: see copy assignment and move assignment for details.
The canonical copy-assignment operator is expected to perform no action on self-assignment , and to return the lhs by reference:
The canonical move assignment is expected to leave the moved-from object in valid state (that is, a state with class invariants intact), and either do nothing or at least leave the object in a valid state on self-assignment, and return the lhs by reference to non-const, and be noexcept:
In those situations where copy assignment cannot benefit from resource reuse (it does not manage a heap-allocated array and does not have a (possibly transitive) member that does, such as a member std::vector or std::string ), there is a popular convenient shorthand: the copy-and-swap assignment operator, which takes its parameter by value (thus working as both copy- and move-assignment depending on the value category of the argument), swaps with the parameter, and lets the destructor clean it up.
This form automatically provides strong exception guarantee , but prohibits resource reuse.
The overloads of operator>> and operator<< that take a std:: istream & or std:: ostream & as the left hand argument are known as insertion and extraction operators. Since they take the user-defined type as the right argument ( b in a@b ), they must be implemented as non-members.
These operators are sometimes implemented as friend functions .
When a user-defined class overloads the function call operator, operator ( ) , it becomes a FunctionObject type. Many standard algorithms, from std:: sort to std:: accumulate accept objects of such types to customize behavior. There are no particularly notable canonical forms of operator ( ) , but to illustrate the usage
When the postfix increment and decrement appear in an expression, the corresponding user-defined function ( operator ++ or operator -- ) is called with an integer argument 0 . Typically, it is implemented as T operator ++ ( int ) , where the argument is ignored. The postfix increment and decrement operator is usually implemented in terms of the prefix version:
Although canonical form of pre-increment/pre-decrement returns a reference, as with any operator overload, the return type is user-defined; for example the overloads of these operators for std::atomic return by value.
Binary operators are typically implemented as non-members to maintain symmetry (for example, when adding a complex number and an integer, if operator+ is a member function of the complex type, then only complex + integer would compile, and not integer + complex ). Since for every binary arithmetic operator there exists a corresponding compound assignment operator, canonical forms of binary operators are implemented in terms of their compound assignments:
Standard algorithms such as std:: sort and containers such as std:: set expect operator < to be defined, by default, for the user-provided types, and expect it to implement strict weak ordering (thus satisfying the Compare requirements). An idiomatic way to implement strict weak ordering for a structure is to use lexicographical comparison provided by std::tie :
Typically, once operator < is provided, the other relational operators are implemented in terms of operator < .
Likewise, the inequality operator is typically implemented in terms of operator == :
When three-way comparison (such as std::memcmp or std::string::compare ) is provided, all six relational operators may be expressed through that:
All six relational operators are automatically generated by the compiler if the three-way comparison operator operator<=> is defined, and that operator, in turn, is generated by the compiler if it is defined as defaulted: Record { name; unsigned int floor; double weight; auto operator<=>(const Record&) = default; }; // records can now be compared with ==, !=, <, <=, >, and >=See for details. | (since C++20) |
User-defined classes that provide array-like access that allows both reading and writing typically define two overloads for operator [ ] : const and non-const variants:
If the value type is known to be a built-in type, the const variant should return by value.
Where direct access to the elements of the container is not wanted or not possible or distinguishing between lvalue c [ i ] = v ; and rvalue v = c [ i ] ; usage, operator[] may return a proxy. see for example std::bitset::operator[] .
To provide multidimensional array access semantics, e.g. to implement a 3D array access a [ i ] [ j ] [ k ] = x ; , operator[] has to return a reference to a 2D plane, which has to have its own operator[] which returns a reference to a 1D row, which has to have operator[] which returns a reference to the element. To avoid this complexity, some libraries opt for overloading operator ( ) instead, so that 3D access expressions have the Fortran-like syntax a ( i, j, k ) = x ;
User-defined classes and enumerations that implement the requirements of BitmaskType are required to overload the bitwise arithmetic operators operator & , operator | , operator ^ , operator~ , operator & = , operator | = , and operator ^ = , and may optionally overload the shift operators operator << operator >> , operator >>= , and operator <<= . The canonical implementations usually follow the pattern for binary arithmetic operators described above.
The operator operator ! is commonly overloaded by the user-defined classes that are intended to be used in boolean contexts. Such classes also provide a user-defined conversion function explicit operator bool ( ) (see std::basic_ios for the standard library example), and the expected behavior of operator ! is to return the value opposite of operator bool .
The following operators are rarely overloaded:
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
C++11 | taking address of incomplete type that overloads address-of was undefined behavior | the behavior is only unspecified |
Common operators | ||||||
---|---|---|---|---|---|---|
a = b | ++a | +a | !a | a == b | a[b] | a(...) |
Special operators | ||||||
converts one type to another related type |
in C++, Operator overloading is a compile-time polymorphism. It is an idea of giving special meaning to an existing operator in C++ without changing its original meaning.
In this article, we will further discuss about operator overloading in C++ with examples and see which operators we can or cannot overload in C++.
C++ has the ability to provide the operators with a special meaning for a data type, this ability is known as operator overloading. Operator overloading is a compile-time polymorphism. For example, we can overload an operator ‘+’ in a class like String so that we can concatenate two strings by just using +. Other example classes where arithmetic operators may be overloaded are Complex Numbers, Fractional Numbers, Big integers, etc.
int a; float b,sum; sum = a + b;
Here, variables “a” and “b” are of types “int” and “float”, which are built-in data types. Hence the addition operator ‘+’ can easily add the contents of “a” and “b”. This is because the addition operator “+” is predefined to add variables of built-in data type only.
Implementation:
// C++ Program to Demonstrate the // working/Logic behind Operator // Overloading class A { statements ; }; int main () { A a1 , a2 , a3 ; a3 = a1 + a2 ; return 0 ; }
In this example, we have 3 variables “a1”, “a2” and “a3” of type “class A”. Here we are trying to add two objects “a1” and “a2”, which are of user-defined type i.e. of type “class A” using the “+” operator. This is not allowed, because the addition operator “+” is predefined to operate only on built-in data types. But here, “class A” is a user-defined type, so the compiler generates an error. This is where the concept of “Operator overloading” comes in.
Now, if the user wants to make the operator “+” add two class objects, the user has to redefine the meaning of the “+” operator such that it adds two class objects. This is done by using the concept of “Operator overloading”. So the main idea behind “Operator overloading” is to use C++ operators with class variables or class objects. Redefining the meaning of operators really does not change their original meaning; instead, they have been given additional meaning along with their existing ones.
// C++ Program to Demonstrate // Operator Overloading #include <iostream> using namespace std ; class Complex { private : int real , imag ; public : Complex ( int r = 0 , int i = 0 ) { real = r ; imag = i ; } // This is automatically called when '+' is used with // between two Complex objects Complex operator + ( Complex const & obj ) { Complex res ; res . real = real + obj . real ; res . imag = imag + obj . imag ; return res ; } void print () { cout << real << " + i" << imag << '\n' ; } }; int main () { Complex c1 ( 10 , 5 ), c2 ( 2 , 4 ); Complex c3 = c1 + c2 ; c3 . print (); }
Operator functions are the same as normal functions. The only differences are, that the name of an operator function is always the operator keyword followed by the symbol of the operator, and operator functions are called when the corresponding operator is used.
#include <iostream> using namespace std ; class Complex { private : int real , imag ; public : Complex ( int r = 0 , int i = 0 ) { real = r ; imag = i ; } void print () { cout << real << " + i" << imag << endl ; } // The global operator function is made friend of this // class so that it can access private members friend Complex operator + ( Complex const & c1 , Complex const & c2 ); }; Complex operator + ( Complex const & c1 , Complex const & c2 ) { return Complex ( c1 . real + c2 . real , c1 . imag + c2 . imag ); } int main () { Complex c1 ( 10 , 5 ), c2 ( 2 , 4 ); Complex c3 = c1 + c2 ; // An example call to "operator+" c3 . print (); return 0 ; }
Almost all operators can be overloaded except a few. Following is the list of operators that cannot be overloaded.
sizeof typeid Scope resolution (::) Class member access operators (.(dot), .* (pointer to member operator)) Ternary or conditional (?:)
We can overload
Unary operators Binary operators Special operators ( [ ], (), etc)
But, among them, there are some operators that cannot be overloaded. They are
Scope resolution operator [Tex](::)[/Tex] Member selection operator Member selection through *
Pointer to a member variable
Operators that can be overloaded | Examples |
---|---|
Binary Arithmetic | +, -, *, /, % |
Unary Arithmetic | +, -, ++, — |
Assignment | =, +=,*=, /=,-=, %= |
Bitwise | & , | , << , >> , ~ , ^ |
De-referencing | (->) |
Dynamic memory allocation, De-allocation | New, delete |
Subscript | [ ] |
Function call | () |
Logical | &, | |, ! |
Relational | >, < , = =, <=, >= |
1. sizeof operator.
This returns the size of the object or datatype entered as the operand. This is evaluated by the compiler and cannot be evaluated during runtime. The proper incrementing of a pointer in an array of objects relies on the sizeof operator implicitly. Altering its meaning using overloading would cause a fundamental part of the language to collapse.
This provides a CPP program with the ability to recover the actually derived type of the object referred to by a pointer or reference. For this operator, the whole point is to uniquely identify a type. If we want to make a user-defined type ‘look’ like another type, polymorphism can be used but the meaning of the typeid operator must remain unaltered, or else serious issues could arise.
This helps identify and specify the context to which an identifier refers by specifying a namespace. It is completely evaluated at runtime and works on names rather than values. The operands of scope resolution are note expressions with data types and CPP has no syntax for capturing them if it were overloaded. So it is syntactically impossible to overload this operator.
The importance and implicit use of class member access operators can be understood through the following example:
// C++ program to demonstrate operator overloading // using dot operator #include <iostream> using namespace std ; class ComplexNumber { private : int real ; int imaginary ; public : ComplexNumber ( int real , int imaginary ) { this -> real = real ; this -> imaginary = imaginary ; } void print () { cout << real << " + i" << imaginary ; } ComplexNumber operator + ( ComplexNumber c2 ) { ComplexNumber c3 ( 0 , 0 ); c3 . real = this -> real + c2 . real ; c3 . imaginary = this -> imaginary + c2 . imaginary ; return c3 ; } }; int main () { ComplexNumber c1 ( 3 , 5 ); ComplexNumber c2 ( 2 , 4 ); ComplexNumber c3 = c1 + c2 ; c3 . print (); return 0 ; }
Explanation:
The statement ComplexNumber c3 = c1 + c2; is internally translated as ComplexNumber c3 = c1.operator+ (c2); in order to invoke the operator function. The argument c1 is implicitly passed using the ‘.’ operator. The next statement also makes use of the dot operator to access the member function print and pass c3 as an argument.
Besides, these operators also work on names and not values and there is no provision (syntactically) to overload them.
The ternary or conditional operator is a shorthand representation of an if-else statement. In the operator, the true/false expressions are only evaluated on the basis of the truth value of the conditional expression.
conditional statement ? expression1 (if statement is TRUE) : expression2 (else)
A function overloading the ternary operator for a class say ABC using the definition
ABC operator ?: (bool condition, ABC trueExpr, ABC falseExpr);
would not be able to guarantee that only one of the expressions was evaluated. Thus, the ternary operator cannot be overloaded.
1) For operator overloading to work, at least one of the operands must be a user-defined class object.
2) Assignment Operator: Compiler automatically creates a default assignment operator with every class. The default assignment operator does assign all members of the right side to the left side and works fine in most cases (this behavior is the same as the copy constructor). See this for more details.
3) Conversion Operator: We can also write conversion operators that can be used to convert one type to another type.
Example:
// C++ Program to Demonstrate the working // of conversion operator #include <iostream> using namespace std ; class Fraction { private : int num , den ; public : Fraction ( int n , int d ) { num = n ; den = d ; } // Conversion operator: return float value of fraction operator float () const { return float ( num ) / float ( den ); } }; int main () { Fraction f ( 2 , 5 ); float val = f ; cout << val << '\n' ; return 0 ; }
Overloaded conversion operators must be a member method. Other operators can either be the member method or the global method.
4) Any constructor that can be called with a single argument works as a conversion constructor, which means it can also be used for implicit conversion to the class being constructed.
// C++ program to demonstrate can also be used for implicit // conversion to the class being constructed #include <iostream> using namespace std ; class Point { private : int x , y ; public : Point ( int i = 0 , int j = 0 ) { x = i ; y = j ; } void print () { cout << "x = " << x << ", y = " << y << '\n' ; } }; int main () { Point t ( 20 , 20 ); t . print (); t = 30 ; // Member x of t becomes 30 t . print (); return 0 ; }
x = 20, y = 20 x = 30, y = 0
Quiz on Operator Overloading
Similar reads.
Find centralized, trusted content and collaborate around the technologies you use most.
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Get early access and see previews of new features.
I have a list of pointers to objects. What I want to do is read the list and store each object in a dynamic array of that object type. So I do this:
This appears to be what I want which means I need the assignment operator but I am a bit confused as to what to put in it and I am getting stack overflow errors, here is what I thought I needed to do:
this is a load of BS I no that but in my head this is what I want it to do but I don't fully understand how I implement it.
If anyone can help or suggest a better way to achieve what I need it would be appreciated.
The reason for doing this is the objects stored on this list are normally iterated through each frame and copied into a temporary object. But the list is not changed during the running of the program, which means I don't need to copy each frame, only once at the start. And I need a dynamic array because I don't know how many objects will be stored in the list.
This could be an operator= implementation:
I don't know your ClassA 's instance variables, so you should implement the internal copying.
On the other hand, when iterating your list, you can copy objects this way:
(**p) returns, first the pointer stored in the iterator, and then dereferences it.
Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more
Post as a guest.
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .
IMAGES
VIDEO
COMMENTS
@K-ballo: His implementation, perhaps by accident, actually covers the problem of self assignment. It checks if the arrays are the same size. If they're not the same size, they can't be the same object.
The assignment operator,"=", is the operator used for Assignment. It copies the right value into the left value. Assignment Operators are predefined to operate only on built-in Data types. Assignment operator overloading is binary operator overloading. Overloading assignment operator in C++ copies all values of one object to another object.
21.12 — Overloading the assignment operator. Alex July 22, 2024. The copy assignment operator (operator=) is used to copy values from one object to another already existing object. As of C++11, C++ also supports "Move assignment". We discuss move assignment in lesson 22.3 -- Move constructors and move assignment.
In those situations where copy assignment cannot benefit from resource reuse (it does not manage a heap-allocated array and does not have a (possibly transitive) member that does, such as a member std::vector or std::string), there is a popular convenient shorthand: the copy-and-swap assignment operator, which takes its parameter by value (thus working as both copy- and move-assignment ...
Correct behavior. CWG 1527. C++11. for assignments to class type objects, the right operand could be an initializer list only when the assignment is defined by a user-defined assignment operator. removed user-defined assignment constraint. CWG 1538. C++11. E1 ={E2} was equivalent to E1 = T(E2) (T is the type of E1), this introduced a C-style cast.
the copy assignment operator selected for every non-static class type (or array of class type) member of T is trivial. A trivial copy assignment operator makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially copy-assignable.
It is idiomatic to provide couple of overloads of the operator[] function - one for const objects and one for non-const objects. The return type of the const member function can be a const& or just a value depending on the object being returned while the return type of the non-const member function is usually a reference.. struct Heap{ int H[100]; int operator [] (int i) const {return H[i ...
It allows you to provide an intuitive interface to users of your class, plus makes it possible for templates to work equally well with classes and built-in/intrinsic types. Operator overloading allows C/C++ operators to have user-defined meanings on user-defined types (classes). Overloaded operators are syntactic sugar for function calls: class ...
The range of a C++ array is from array[0] to array[size - 1]. However, C++ supports positive and negative subscripts. Negative subscripts must fall within array boundaries; if they do not, the results are unpredictable. ... Prerequisite: Operator Overloading The assignment operator,"=", is the operator used for Assignment. It copies the right ...
C++ compiler implicitly provides a copy constructor, if no copy constructor is defined in the class. A bitwise copy gets created, if the Assignment operator is not overloaded. Consider the following C++ program. Explanation: Here, t2 = t1; calls the assignment operator, same as t2.operator= (t1); and Test t3 = t1; calls the copy constructor ...
This can result in changes done to one class object to affect all copies of this object. A solution to this can be to overload the = operator. Given the example below, with an attempt to create a dynamic array class, why does making changes to MyArray1 change MyArray2: Array Class: #include <iostream>.
3) built-in candidates: For operator,, the unary operator &, and operator->, the set of built-in candidates is empty.For other operators built-in candidates are the ones listed in built-in operator pages as long as all operands can be implicitly converted to their parameters. If any built-in candidate has the same parameter list as a non-member candidate or rewritten non-member candidate ...
In those situations where copy assignment cannot benefit from resource reuse (it does not manage a heap-allocated array and does not have a (possibly transitive) member that does, such as a member std::vector or std::string), there is a popular convenient shorthand: the copy-and-swap assignment operator, which takes its parameter by value (thus working as both copy- and move-assignment ...
Why operator[] returns a reference. Let's take a closer look at how list[2] = 3 evaluates. Because the subscript operator has a higher precedence than the assignment operator, list[2] evaluates first.list[2] calls operator[], which we've defined to return a reference to list.m_list[2].Because operator[] is returning a reference, it returns the actual list.m_list[2] array element.
The C++ language does not allow defining operator overloads to anything other than classes. Some alternatives: Wrap the array as a member of a class and define assignment operator for that class. This is what std::array is, although it uses the implicit assignment operator which behaves differently from your operator (you cannot assign arrays ...
Prerequisite: Operator Overloading The assignment operator,"=", is the operator used for Assignment. It copies the right value into the left value. Assignment Operators are predefined to operate only on built-in Data types. Assignment operator overloading is binary operator overloading.Overloading assignment operator in C++ copies all values of one
New operators such as **, <>, or &| cannot be created. The overloads of operators &&, ||, and , (comma) lose their special properties: short-circuit evaluation and sequencing. The overload of operator -> must either return a raw pointer or return an object (by reference or by value), for which operator -> is in turn overloaded.
Assignment operator for dynamic array in C++. 0. Overloaded assignment operator with arrays. 0. Dynamic array implementaion c++ , Overloading assignment operator. Hot Network Questions `uname -r` shows kernel marked as `vmlinuz.old` in `/boot` partition