Difference between revisions of "cpp/language/operator other"
(Added CWG issue #1805 DR.) |
(Added CWG issue #2226 DR, also updated the corresponding wording.) |
||
Line 173: | Line 173: | ||
}} | }} | ||
− | + | @2@ Otherwise, if {{tt|E2}} or {{tt|E3}} are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv T for the remainder of this section, where cv is the union of cv1 and cv2. | |
− | @2@ Otherwise, if {{tt|E2}} or {{tt|E3}} are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv T for the remainder of this section, where cv is the union of cv1 and cv2. | + | @3@ Otherwise, if {{tt|E2}} and {{tt|E3}} have different types, at least one of which is a (possibly cv-qualified) class type, or both are glvalues of the same value category and have the same type except for cv-qualification, then an attempt is made to form an {{rlp|implicit conversion|implicit conversion sequence}} ignoring member access, whether an operand is a bit-field, or whether a conversion function is deleted from each of the operands to the ''target type'' determined by the other operand, as described below. An operand (call it {{tt|X}}) of type {{tt|TX}} can be converted to the ''target type'' of the other operand (call it {{tt|Y}}) of type {{tt|TY}} as follows: |
− | @3@ Otherwise, if {{tt|E2}} and {{tt|E3}} have different types, at least one of which is a (possibly cv-qualified) class type, or both are glvalues of the same value category and have the same type except for cv-qualification, then an attempt is made to form an {{rlp|implicit conversion|implicit conversion sequence | + | :@3.1@ If {{tt|Y}} is an lvalue, the target type is {{tt|TY&}}, and the reference must bind directly to {{rev inl|until=c++11|an lvalue}}{{rev inl|since=c++11|a glvalue}}; |
− | :@3.1@ If {{tt|Y}} is an lvalue, the target type is {{tt|TY&}}, and the reference must bind directly to an lvalue; | + | |
+ | {{rrev|since=c++11| | ||
:@3.2@ If {{tt|Y}} is an xvalue, the target type is {{tt|TY&&}}, and the reference must bind directly; | :@3.2@ If {{tt|Y}} is an xvalue, the target type is {{tt|TY&&}}, and the reference must bind directly; | ||
− | :@3.3@ If {{tt|Y}} is a prvalue, or if neither the above conversion sequences can be formed and at least one of {{tt|TX}} and {{tt|TY}} is a (possibly cv-qualified) class type, | + | }} |
+ | :@3.3@ If {{tt|Y}} is {{rev inl|until=c++11|an rvalue}}{{rev inl|since=c++11|a prvalue}}, or if neither the above conversion sequences can be formed and at least one of {{tt|TX}} and {{tt|TY}} is a (possibly cv-qualified) class type, | ||
::@3.3.1@ if {{tt|TX}} and {{tt|TY}} are the same class type (ignoring cv-qualification) and {{tt|TY}} is at least as cv-qualified as {{tt|TX}}, the target type is {{tt|TY}}, | ::@3.3.1@ if {{tt|TX}} and {{tt|TY}} are the same class type (ignoring cv-qualification) and {{tt|TY}} is at least as cv-qualified as {{tt|TX}}, the target type is {{tt|TY}}, | ||
::@3.3.2@ otherwise, if {{tt|TY}} is a base class of {{tt|TX}}, the target type is {{tt|TY}} with the cv-qualifiers of {{tt|TX}}: | ::@3.3.2@ otherwise, if {{tt|TY}} is a base class of {{tt|TX}}, the target type is {{tt|TY}} with the cv-qualifiers of {{tt|TX}}: | ||
Line 188: | Line 190: | ||
using T = const B; | using T = const B; | ||
− | A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B | + | A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B, Target = const A |
}} | }} | ||
::@3.3.3@ otherwise, the target type is the type that {{tt|Y}} would have after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer {{rlp|implicit conversion|standard conversions}} | ::@3.3.3@ otherwise, the target type is the type that {{tt|Y}} would have after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer {{rlp|implicit conversion|standard conversions}} | ||
Line 321: | Line 323: | ||
{{dr list item|wg=cwg|dr=1560|std=C++98|before=void operand in conditional expressions caused gratuitous lvalue-to-<br>rvalue conversion on the other operand, always resulting in rvalue|after=a conditional expression<br>with a void can be lvalue}} | {{dr list item|wg=cwg|dr=1560|std=C++98|before=void operand in conditional expressions caused gratuitous lvalue-to-<br>rvalue conversion on the other operand, always resulting in rvalue|after=a conditional expression<br>with a void can be lvalue}} | ||
{{dr list item|wg=cwg|dr=1805|std=C++98|before=in case 3.3.3) of conditional expressions, array-to-pointer<br>and function-to-pointer conversions were not applied|after=apply these conversions}} | {{dr list item|wg=cwg|dr=1805|std=C++98|before=in case 3.3.3) of conditional expressions, array-to-pointer<br>and function-to-pointer conversions were not applied|after=apply these conversions}} | ||
− | + | {{dr list item|wg=cwg|dr=1895|std=C++98|before=unclear if deleted or inaccessible conversion function prevents<br>conversion in conditional expressions, and conversions from<br>base class to derived class prvalue were not considered<!-- or, as summarized in https://github.com/llvm-mirror/clang/blob/0d385d/test/SemaCXX/conditional-expr.cpp#L115, "for rvalues, hierarchy takes precedence over other conversions" -->|after=handled like<br>overload resolution}} | |
− | {{dr list item|wg=cwg|dr=1895|std=C++ | + | {{dr list item|wg=cwg|dr=1932|std=C++98|before=same-type bit-fields were missing in conditional expressions|after=handled by underlying types}} |
+ | {{dr list item|wg=cwg|dr=2226|std=C++11|before=when determining the target type of the other operand,<br>reference could not bind to an xvalue if that operand is an lvalue|after=allowed}} | ||
{{dr list end}} | {{dr list end}} | ||
Revision as of 22:24, 27 June 2022
Operator name |
Syntax | Overloadable | Prototype examples (for class T) | |
---|---|---|---|---|
Inside class definition | Outside class definition | |||
function call | a(a1, a2)
|
Yes | R T::operator()(Arg1 &a1, Arg2 &a2, ...); | N/A |
comma | a, b
|
Yes | T2& T::operator,(T2 &b); | T2& operator,(const T &a, T2 &b); |
conditional operator | a ? b : c
|
No | N/A | N/A |
Contents |
Explanation
The function call operator provides function semantics for any object.
The conditional operator (colloquially referred to as ternary conditional) checks the boolean value of the first expression and, depending on the resulting value, evaluates and returns either the second or the third expression.
Built-in function call operator
The function call expressions have the form
E ( A1, A2, A3, ...)
|
|||||||||
where
- E is an expression that names a function
- A1
,
A2,
A3,
... is a possibly empty list of arbitrary expressions or braced-init-lists(since C++11), except the comma operator is not allowed at the top level to avoid ambiguity.
The expression that names the function can be
The function (or member) name specified by E
can be overloaded, overload resolution rules used to decide which overload is to be called.
If E
specifies a member function, it may be virtual, in which case the final overrider of that function will be called, using dynamic dispatch at runtime.
To call the function,
The expression |
(until C++17) |
The expression |
(since C++17) |
Each function parameter is initialized with its corresponding argument after implicit conversion if necessary. If there is no corresponding argument, the corresponding default argument is used, and if there is none, the program is ill-formed. If the call is made to a member function, then the this pointer to current object is converted as if by explicit cast to the this pointer expected by the function. The initialization and destruction of each parameter occurs in the context of the caller, which means, for example, that if constructor of a parameter throws an exception, the exception handlers defined within the function, even as a function-try block, are not considered. If the function is a variadic function, default argument promotions are applied to all arguments matched by the ellipsis parameter. It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.
The return type of a function call expression is the return type of the chosen function, decided using static binding (ignoring the virtual
keyword), even if the overriding function that's actually called returns a different type. This allows the overriding functions to return pointers or references to classes that are derived from the return type returned by the base function, i.e. C++ supports covariant return types. If E
specifies a destructor, the return type is void.
When an object of class type The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). This allows objects of small class types, such as std::complex or std::span, to be passed to or returned from functions in registers. |
(since C++17) |
The value category of a function call expression is lvalue if the function returns an lvalue reference or an rvalue reference to function, is an xvalue if the function returns an rvalue reference to object, and is a prvalue otherwise. If the function call expression is a prvalue of object type, it must have complete type except when the prvalue is not materialized, such as(since C++17) when used as the operand of decltype
(or as the right operand of a built-in comma operator expression that is the operand of decltype
).
Function call expression is similar in syntax to value initialization T(), to function-style cast expression T(A1), and to direct initialization of a temporary T(A1, A2, A3, ...), where T
is the name of a type.
#include <cstdio> struct S { int f1(double d) { return printf("%f \n", d); // variable argument function call } int f2() { return f1(7); // member function call, same as this->f1() // integer argument converted to double } }; void f() { puts("function called"); // function call } int main() { f(); // function call S s; s.f2(); // member function call }
Output:
function called 7.000000
Built-in comma operator
The comma operator expressions have the form
E1 , E2
|
|||||||||
In a comma expression E1, E2, the expression E1
is evaluated, its result is discarded (although if it has class type, it won't be destroyed until the end of the containing full expression), and its side effects are completed before evaluation of the expression E2
begins (note that a user-defined operator,
cannot guarantee sequencing)(until C++17).
The type, value, and value category of the result of the comma expression are exactly the type, value, and value category of the second operand, E2
. If E2
is a temporary expression(since C++17), the result of the expression is that temporary expression(since C++17). If E2
is a bit-field, the result is a bit-field.
The comma in various comma-separated lists, such as function argument lists (f(a, b, c)) and initializer lists int a[] = {1, 2, 3}, is not the comma operator. If the comma operator needs to be used in such contexts, it has to be parenthesized: f(a, (n++, n + b), c)
Using an unparenthesized comma expression as second (right) argument of a subscript operator is deprecated. For example, a[b, c] is deprecated and a[(b, c)] is not. |
(since C++20) (until C++23) |
An unparenthesized comma expression cannot be second (right) argument of a subscript operator. For example, a[b, c] is either ill-formed or equivalent to a.operator[](b, c). Parentheses are needed to for using a comma expression as the subscript, e.g., a[(b, c)]. |
(since C++23) |
Output:
n = 2 m = 7
Conditional operator
The conditional operator expressions have the form
E1 ? E2 : E3
|
|||||||||
The first operand of the conditional operator is evaluated and contextually converted to bool. After both the value evaluation and all side effects of the first operand are completed, if the result was true, the second operand is evaluated. If the result was false, the third operand is evaluated.
The type and value category of the conditional expression E1 ? E2 : E3 are determined according to the following rules:
E2
or E3
has type void, then one of the following must be true, or the program is ill-formed:E2
or E3
(but not both) is a (possibly parenthesized) throw-expression. The result of the conditional operator has the type and the value category of the other expression. If the other expression is a bit-field, the result is a bit-field. Such conditional operator was commonly used in C++11 constexpr programming prior to C++14.
std::string str = 2 + 2 == 4 ? "OK" : throw std::logic_error("2 + 2 != 4");
E2
and E3
are of type void (including the case when they are both throw-expressions). The result is a prvalue of type void.
2 + 2 == 4 ? throw 123 : throw 456;
E2
or E3
are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv T for the remainder of this section, where cv is the union of cv1 and cv2.E2
and E3
have different types, at least one of which is a (possibly cv-qualified) class type, or both are glvalues of the same value category and have the same type except for cv-qualification, then an attempt is made to form an implicit conversion sequence ignoring member access, whether an operand is a bit-field, or whether a conversion function is deleted from each of the operands to the target type determined by the other operand, as described below. An operand (call it X
) of type TX
can be converted to the target type of the other operand (call it Y
) of type TY
as follows:Y
is an lvalue, the target type is TY&
, and the reference must bind directly to an lvalue(until C++11)a glvalue(since C++11);
3.2) If
Y is an xvalue, the target type is TY&& , and the reference must bind directly; |
(since C++11) |
Y
is an rvalue(until C++11)a prvalue(since C++11), or if neither the above conversion sequences can be formed and at least one of TX
and TY
is a (possibly cv-qualified) class type,TX
and TY
are the same class type (ignoring cv-qualification) and TY
is at least as cv-qualified as TX
, the target type is TY
,TY
is a base class of TX
, the target type is TY
with the cv-qualifiers of TX
:
struct A {}; struct B : A {}; using T = const B; A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B, Target = const A
Y
would have after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversionsE2
to target type of E3
and E3
to target type of E2
), or only one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.E2
and E3
are glvalues of the same type and the same value category, then the result has the same type and value category, and is a bit-field if at least one of E2
and E3
is a bit-field.E2
and E3
do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is performed using the built-in candidates below to attempt to convert the operands to built-in types. If the overload resolution fails, the program is ill-formed. Otherwise, the selected conversions are applied and the converted operands are used in place of the original operands for step 6.E2
and E3
now have the same type, the result is a prvalue of that type designating a temporary object(until C++17)whose result object is(since C++17) copy-initialized from whatever operand was selected after evaluating E1
.E2
and E3
have arithmetic or enumeration type: the usual arithmetic conversions are applied to bring them to common type, and that type is the result.E2
and E3
are pointers, or one is a pointer and the other is a null pointer constant, then pointer conversions and qualification conversions are applied to bring them to common type, and that type is the result.
int* intPtr; using Mixed = decltype(true ? nullptr : intPtr); static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int*
E2
and E3
are pointers to members, or one is a pointer to member and the other is a null pointer constant, then pointer-to-member conversions and qualification conversions are applied to bring them to common type, and that type is the result.
struct A { int* m_ptr; } a; int* A::* memPtr = &A::m_ptr; // memPtr is a pointer to member m_ptr of A // memPtr makes nullptr as type of pointer to member m_ptr of A static_assert(std::is_same_v<decltype(false ? memPtr : nullptr), int*A::*>); // a.*memPtr is now just pointer to int and nullptr also becomes pointer to int static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
E2
and E3
are null pointer constants, and at least one of which is of type std::nullptr_t, then the result's type is std::nullptr_t
.This section is incomplete Reason: any chance to make this more readable without losing the fine point? At the very least, a one-line micro-example for each bullet point would help tremendously |
For every pair of promoted arithmetic types L
and R
and for every type P
, where P
is a pointer, pointer-to-member, or scoped enumeration type, the following function signatures participate in the overload resolution performed in step 5 of the rules above:
LR operator?:(bool, L, R); |
||
P operator?:(bool, P, P); |
||
where LR is the result of usual arithmetic conversions performed on L
and R
. The operator “?:
” cannot be overloaded, these function signatures only exist for the purpose of overload resolution.
The return type of a conditional operator is also accessible as the binary type trait std::common_type.
#include <string> #include <iostream> struct Node { Node* next; int data; // deep-copying copy constructor Node(const Node& other) : next(other.next ? new Node(*other.next) : NULL) , data(other.data) {} Node(int d) : next(NULL), data(d) {} ~Node() { delete next ; } }; int main() { // simple rvalue example int n = 1 > 2 ? 10 : 11; // 1 > 2 is false, so n = 11 // simple lvalue example int m = 10; (n == m ? n : m) = 7; // n == m is false, so m = 7 //output the result std::cout << "n = " << n << "\nm = " << m; }
Output:
n = 11 m = 7
Standard library
Many classes in the standard library overload operator()
to be used as function objects.
deletes the object or array (public member function of std::default_delete<T> )
| |
returns the sum of two arguments (public member function of std::plus<T> )
| |
returns the difference between two arguments (public member function of std::minus<T> )
| |
returns the product of two arguments (public member function of std::multiplies<T> )
| |
returns the result of the division of the first argument by the second argument (public member function of std::divides<T> )
| |
returns the remainder from the division of the first argument by the second argument (public member function of std::modulus<T> )
| |
returns the negation of the argument (public member function of std::negate<T> )
| |
checks if the arguments are equal (public member function of std::equal_to<T> )
| |
checks if the arguments are not equal (public member function of std::not_equal_to<T> )
| |
checks if the first argument is greater than the second (public member function of std::greater<T> )
| |
checks if the first argument is less than the second (public member function of std::less<T> )
| |
checks if the first argument is greater than or equal to the second (public member function of std::greater_equal<T> )
| |
checks if the first argument is less than or equal to the second (public member function of std::less_equal<T> )
| |
returns the logical AND of the two arguments (public member function of std::logical_and<T> )
| |
returns the logical OR of the two arguments (public member function of std::logical_or<T> )
| |
returns the logical NOT of the argument (public member function of std::logical_not<T> )
| |
returns the result of bitwise AND of two arguments (public member function of std::bit_and<T> )
| |
returns the result of bitwise OR of two arguments (public member function of std::bit_or<T> )
| |
returns the result of bitwise XOR of two arguments (public member function of std::bit_xor<T> )
| |
returns the logical complement of the result of a call to the stored predicate (public member function of std::unary_negate<Predicate> )
| |
returns the logical complement of the result of a call to the stored predicate (public member function of std::binary_negate<Predicate> )
| |
calls the stored function (public member function of std::reference_wrapper<T> )
| |
invokes the target (public member function of std::function<R(Args...)> )
| |
lexicographically compares two strings using this locale's collate facet (public member function of std::locale )
| |
compares two values of type value_type (public member function of std::map<Key,T,Compare,Allocator>::value_compare )
| |
compares two values of type value_type (public member function of std::multimap<Key,T,Compare,Allocator>::value_compare )
| |
executes the function (public member function of std::packaged_task<R(Args...)> )
| |
advances the engine's state and returns the generated value (public member function of std::linear_congruential_engine<UIntType,a,c,m> )
| |
(C++11) |
generates the next random number in the distribution (public member function of std::uniform_int_distribution<IntType> )
|
The comma operator is not overloaded by any class in the standard library. The boost library uses operator, in boost.assign, boost.spirit, and other libraries. The database access library SOCI also overloads operator,.
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 446 | C++98 | it was unspecified whether a temporary is created for an lvalue-to-rvalue conversion on the conditional operator |
always creates a temporary if the operator returns a class rvalue |
CWG 462 | C++98 | if the second operand of a comma expression is a temporary, it was unspecified whether its lifetime will be extended when the result of the comma expression is bound to a reference |
the result of the comma expression is the temporary in this case (hence its lifetime is extended) |
CWG 587 | C++98 | when the second and third operands of a conditional expression are lvalues of the same type except for cv-qualification, the result was an lvalue if these operands have class types or an rvalue otherwise |
the result is always an lvalue in this case |
CWG 1029 | C++98 | the type of a destructor call was unspecified | specified as void |
CWG 1550 | C++98 | parenthesized throw-expression were not allowed in conditional expressions if other operand is non-void |
accepted |
CWG 1560 | C++98 | void operand in conditional expressions caused gratuitous lvalue-to- rvalue conversion on the other operand, always resulting in rvalue |
a conditional expression with a void can be lvalue |
CWG 1805 | C++98 | in case 3.3.3) of conditional expressions, array-to-pointer and function-to-pointer conversions were not applied |
apply these conversions |
CWG 1895 | C++98 | unclear if deleted or inaccessible conversion function prevents conversion in conditional expressions, and conversions from base class to derived class prvalue were not considered |
handled like overload resolution |
CWG 1932 | C++98 | same-type bit-fields were missing in conditional expressions | handled by underlying types |
CWG 2226 | C++11 | when determining the target type of the other operand, reference could not bind to an xvalue if that operand is an lvalue |
allowed |
See also
Operator precedence Operator overloading
Common operators | ||||||
---|---|---|---|---|---|---|
assignment | increment decrement |
arithmetic | logical | comparison | member access |
other |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
function call |
a(...) | ||||||
comma | ||||||
a, b | ||||||
conditional | ||||||
a ? b : c | ||||||
Special operators | ||||||
static_cast converts one type to another related type |
C documentation for Other operators
|