Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/operator other"

From cppreference.com
< cpp‎ | language
(Restored the 'since C++11' status regarding deleted conversion functions for CWG issue #1895.)
(Added CWG issue #2865 DR.)
 
(18 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
{{title|Other operators}}
 
{{title|Other operators}}
 
{{cpp/language/expressions/navbar}}
 
{{cpp/language/expressions/navbar}}
{| class="wikitable" style="font-size:85%;"
+
{|class="wikitable" style="font-size:85%;"
 
|-
 
|-
! rowspan="2" | Operator<br>name
+
!rowspan="2"|Operator<br>name
! rowspan="2" style="width: 9%" | Syntax
+
!rowspan="2" style="width: 9%"|Syntax
! rowspan="2" | {{rlp|operators|Over&#8203;load&#8203;able}}
+
!rowspan="2"|{{rlp|operators|Over&#8203;load&#8203;able}}
! colspan="2" | Prototype examples (for {{c|class T}})
+
!colspan="2"|Prototype examples (for {{c/core|class T}})
 
|-
 
|-
! Inside class definition
+
!Inside class definition
! Outside class definition
+
!Outside class definition
 
|-
 
|-
| function call
+
|function call
| {{tt|a(a1, a2)}}
+
|{{tt|a(a1, a2)}}
| {{yes}}
+
|{{yes}}
| {{c|R T::operator()(Arg1 &a1, Arg2 &a2, ...);}}
+
|{{c|R T::operator()(Arg1 &a1, Arg2 &a2, ...);}}
| {{n/a}}
+
|{{n/a}}
 
|-
 
|-
| comma
+
|comma
| {{tt|a, b}}
+
|{{tt|a, b}}
| {{yes}}
+
|{{yes}}
| {{c|T2& T::operator,(T2 &b);}}
+
|{{c|T2& T::operator,(T2 &b);}}
| {{c|T2& operator,(const T &a, T2 &b);}}
+
|{{c|T2& operator,(const T &a, T2 &b);}}
 
|-
 
|-
| conditional operator
+
|conditional operator
| {{tt|a ? b : c}}
+
|{{tt|a ? b : c}}
| {{no}}
+
|{{no}}
| {{n/a}}
+
|{{n/a}}
| {{n/a}}
+
|{{n/a}}
 
|}
 
|}
  
===Explanation===
 
 
The ''function call'' operator provides function semantics for any object.
 
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.
+
The ''conditional operator'' (colloquially referred to as ''ternary conditional''{{sep}}) 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
+
  
 +
===Built-in function call operator===
 +
Function call expressions have the following form:
 
{{sdsc begin}}
 
{{sdsc begin}}
{{sdsc|{{spar|E}} {{ttb|(}} {{spar|A1}}{{ttb|,}} {{spar|A2}}{{ttb|,}} {{spar|A3}}{{ttb|,}}...{{ttb|)}}}}  
+
{{sdsc|{{spar sep|function}}{{ttb|(}}{{spar|arg1}}{{ttb|,}} {{spar|arg2}}{{ttb|,}} {{spar|arg3}}{{ttb|,}}...{{ttb|)}}}}  
 
{{sdsc end}}
 
{{sdsc end}}
where
 
* {{spar|E}} is an expression that names a {{rlp|functions|function}}
 
* {{spar|A1}}{{ttb|,}} {{spar|A2}}{{ttb|,}} {{spar|A3}}{{ttb|,}}... is a possibly empty list of arbitrary expressions{{rev inl|since=c++11| or braced-init-lists}}, except the comma operator is not allowed at the top level to avoid ambiguity.
 
  
The expression that names the function can be
+
{{par begin}}
@a@ lvalue expression that refers to a function
+
{{par|{{spar|function}}|an expression function type or function pointer type}}
@b@ pointer to function
+
{{par|{{spar|arg1}}{{ttb|,}} {{spar|arg2}}{{ttb|,}} {{spar|arg3}}{{ttb|,}}...|a possibly empty list of arbitrary expressions{{rev inl|since=c++11| or {{rlp|initialization|brace-enclosed initializer lists}}}}, except the comma operator is not allowed at the top level to avoid ambiguity}}
@c@ explicit {{rlp|operator member access|class member access}} expression that selects a member function
+
{{par end}}
@d@ implicit class member access expression, e.g. member function name used within another member function.
+
  
The function (or member) name specified by {{tt|E}} can be overloaded, {{rlp|overload resolution}} rules used to decide which overload is to be called.
+
For a call to a non-member function or to a {{rlp|static|static member function}}, {{spar|function}} can be an lvalue that refers to a function (in which case the {{rlpsd|implicit conversion#Function-to-pointer conversion}} is suppressed), or a prvalue of function pointer type.
  
If {{tt|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.
+
The function (or member) name specified by {{spar|function}} can be overloaded, {{rlp|overload resolution}} rules used to decide which overload is to be called.
 +
 
 +
If {{spar|function}} 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,  
 
To call the function,  
 
{{rrev multi|until1=c++17|rev1=
 
{{rrev multi|until1=c++17|rev1=
The expression {{tt|E}} as well as all expressions {{tt|A1}}, {{tt|A2}}, {{tt|A3}}, etc, provided as arguments are evaluated in arbitrary order, {{rlp|eval order|unsequenced}} with respect to each other.
+
The expression {{spar|function}} as well as all expressions {{spar|arg1}}, {{spar|arg2}}, {{spar|arg3}}, etc, provided as arguments are evaluated in arbitrary order, {{rlp|eval order|unsequenced}} with respect to each other.
 
|rev2=
 
|rev2=
The expression {{tt|E}} is sequenced before each of the expressions {{tt|A1}}, {{tt|A2}}, {{tt|A3}} as well as default arguments, if any. The argument expressions are evaluated in arbitrary order, {{rlp|eval order|indeterminately sequenced}} with respect to each other.
+
{{spar|function}} is sequenced before each of {{spar|arg1}}, {{spar|arg2}}, {{spar|arg3}} as well as {{rlp|default arguments}}, if any. The argument expressions are evaluated in arbitrary order, {{rlp|eval order|indeterminately sequenced}} with respect to each other.
 
}}
 
}}
Each function parameter is initialized with its corresponding argument after {{rlp|implicit conversion}} if necessary. If there is no corresponding argument, the corresponding {{rlp|default arguments|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 {{c|this}} pointer to current object is converted as if by explicit cast to the {{c|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, {{rlp|variadic arguments#Default conversions|default argument promotions}} are applied to all arguments matched by the ellipsis parameter. It is implementation-defined whether the {{rlp|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 {{tt|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 [[enwiki:Covariant return type|covariant return types]]. If {{tt|E}} specifies a destructor, the return type is {{c|void}}.
+
Each function parameter is initialized with its corresponding argument after {{rlp|implicit conversion}} if necessary.
 +
* If there is no corresponding argument, the corresponding {{rlp|default arguments|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 {{c|this}} pointer to current object is converted as if by explicit cast to the {{c|this}} pointer expected by the function.
 +
* The initialization and destruction of each parameter occurs in the context of the {{rlp|expressions#Full-expressions|full-expression}} where the function call appears, which means, for example, that if a constructor or destructor of a parameter throws an exception, the {{rlp|try#Function try block|function {{c/core|try}} blocks}} of the called function are not considered.
 +
 
 +
If the function is a variadic function, {{rlp|variadic arguments#Default conversions|default argument promotions}} are applied to all arguments matched by the ellipsis parameter.
 +
 
 +
It is implementation-defined whether a parameter is destroyed when the function in which it is defined exits or at the end of the enclosing full-expression. Parameters are always destroyed in the reverse order of their construction.
 +
 
 +
The return type of a function call expression is the return type of the chosen function, decided using static binding (ignoring the {{c/core|virtual}} keyword), even if the overriding function that is 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 {{enwiki|Covariant return type|covariant return types}}. If {{spar|function}} specifies a destructor, the return type is {{c/core|void}}.
  
 
{{rrev|since=c++17|
 
{{rrev|since=c++17|
Line 73: Line 77:
 
}}
 
}}
  
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 {{rev inl|since=c++17|when the prvalue is not materialized, such as}} when used as the operand of {{rlpt|decltype}} (or as the right operand of a [[#Built-in comma operator|built-in comma operator expression]] that is the operand of {{tt|decltype}}).
+
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 {{rev inl|since=c++17|when the prvalue is not materialized, such as}} when used as the operand of {{rlpt|decltype}} (or as the right operand of a [[#Built-in comma operator|built-in comma operator]] that is the operand of {{c/core|decltype}}).
  
 
Function call expression is similar in syntax to value initialization {{c|T()}}, to {{rlp|explicit cast|function-style cast}} expression {{c|T(A1)}}, and to direct initialization of a temporary {{c|T(A1, A2, A3, ...)}}, where {{tt|T}} is the name of a type.
 
Function call expression is similar in syntax to value initialization {{c|T()}}, to {{rlp|explicit cast|function-style cast}} expression {{c|T(A1)}}, and to direct initialization of a temporary {{c|T(A1, A2, A3, ...)}}, where {{tt|T}} is the name of a type.
  
 
{{example
 
{{example
|
 
 
|code=
 
|code=
 
#include <cstdio>
 
#include <cstdio>
Line 112: Line 115:
 
}}
 
}}
  
====Built-in comma operator====
+
===Built-in comma operator===
The comma operator expressions have the form  
+
Comma expressions have the following form:
 
+
 
{{sdsc begin}}
 
{{sdsc begin}}
 
{{sdsc|{{spar|E1}} {{ttb|,}} {{spar|E2}}}}  
 
{{sdsc|{{spar|E1}} {{ttb|,}} {{spar|E2}}}}  
 
{{sdsc end}}
 
{{sdsc end}}
  
In a comma expression {{c|E1, E2}}, the expression {{tt|E1}} is evaluated, its result is {{rlp|expressions#Discarded-value expressions|discarded}} (although if it has class type, it won't be destroyed {{rlp|lifetime#Temporary object lifetime|until the end of the containing full expression}}), and its side effects are completed before evaluation of the expression {{tt|E2}} begins {{rev inl|until=c++17|(note that a user-defined {{tt|operator,}} cannot guarantee sequencing)}}.  
+
In a comma expression {{c|E1, E2}}, the expression {{c|E1}} is evaluated, its result is {{rlp|expressions#Discarded-value expressions|discarded}} (although if it has class type, it won't be destroyed {{rlp|lifetime#Temporary object lifetime|until the end of the containing full expression}}), and its side effects are completed before evaluation of the expression {{c|E2}} begins {{rev inl|until=c++17|(note that a user-defined {{tt|operator,}} cannot guarantee sequencing)}}.  
  
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, {{tt|E2}}. If {{tt|E2}} is a temporary {{rev inl|since=c++17|expression}}, the result of the expression is that temporary {{rev inl|since=c++17|expression}}. If {{tt|E2}} is a bit-field, the result is a bit-field.
+
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, {{c|E2}}. If {{c|E2}} is a temporary {{rev inl|since=c++17|expression}}, the result of the expression is that temporary {{rev inl|since=c++17|expression}}. If {{c|E2}} is a bit-field, the result is a bit-field.
  
The comma in various comma-separated lists, such as function argument lists ({{c|f(a, b, c)}}) and initializer lists {{c|1=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: {{c|f(a, (n++, n + b), c)}}
+
The comma in various comma-separated lists, such as function argument lists ({{c|f(a, b, c)}}) and initializer lists {{c|1=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: {{c|f(a, (n++, n + b), c)}}.
  
 
{{rrev multi
 
{{rrev multi
Line 133: Line 135:
 
An unparenthesized comma expression cannot be second (right) argument of a {{rlp|operator member access#Built-in subscript operator|subscript operator}}. For example, {{c|a[b, c]}} is either ill-formed or equivalent to {{c|a.operator[](b, c)}}.
 
An unparenthesized comma expression cannot be second (right) argument of a {{rlp|operator member access#Built-in subscript operator|subscript operator}}. For example, {{c|a[b, c]}} is either ill-formed or equivalent to {{c|a.operator[](b, c)}}.
  
Parentheses are needed to for using a comma expression as the subscript, e.g., {{c|a[(b, c)]}}.
+
Parentheses are needed when using a comma expression as the subscript, e.g., {{c|a[(b, c)]}}.
 
}}
 
}}
  
 
{{example
 
{{example
|
 
 
|code=
 
|code=
 
#include <iostream>
 
#include <iostream>
Line 143: Line 144:
 
int main()
 
int main()
 
{
 
{
 +
    // comma is often used to execute more than one expression
 +
    // where the language grammar allows only one expression:
 +
   
 +
    // * in the third component of the for loop
 +
    for (int i = 0, j = 10; i <= j; ++i, --j)
 +
    //            ^list separator      ^comma operator
 +
        std::cout << "i = " << i << " j = " << j << '\n';
 +
   
 +
    // * in a return statement
 +
    // return log("an error!"), -1;
 +
   
 +
    // * in an initializer expression
 +
    // MyClass(const Arg& arg)
 +
    // : member{ throws_if_bad(arg), arg }
 +
   
 +
    // etc.
 +
   
 +
    // comma operators can be chained; the result of the last
 +
    // (rightmost) expression is the result of the whole chain:
 
     int n = 1;
 
     int n = 1;
 
     int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n);
 
     int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n);
 +
   
 +
    // m is now 6
 
     std::cout << "m = " << (++m, m) << '\n';
 
     std::cout << "m = " << (++m, m) << '\n';
 
}
 
}
| output=
+
|output=
 +
i = 0 j = 10
 +
i = 1 j = 9
 +
i = 2 j = 8
 +
i = 3 j = 7
 +
i = 4 j = 6
 +
i = 5 j = 5
 
n = 2
 
n = 2
 
m = 7
 
m = 7
 
}}
 
}}
  
====Conditional operator====
+
===Conditional operator===
 
The conditional operator expressions have the form  
 
The conditional operator expressions have the form  
  
Line 159: Line 187:
 
{{sdsc end}}
 
{{sdsc end}}
  
The first operand of the conditional operator is evaluated and {{rlp|implicit conversion|contextually converted}} to {{c|bool}}. After both the value evaluation and all side effects of the first operand are completed, if the result was {{c|true}}, the second operand is evaluated. If the result was {{c|false}}, the third operand is evaluated.
+
The first operand of the conditional operator is evaluated and {{rlp|implicit conversion|contextually converted}} to {{c/core|bool}}. After both the value evaluation and all side effects of the first operand are completed, if the result was {{c|true}}, the second operand is evaluated. If the result was {{c|false}}, the third operand is evaluated.
  
 
The type and value category of the conditional expression {{c|E1 ? E2 : E3}} are determined according to the following rules:
 
The type and value category of the conditional expression {{c|E1 ? E2 : E3}} are determined according to the following rules:
  
@1@ If either {{tt|E2}} or {{tt|E3}} has type {{c|void}}, then one of the following must be true, or the program is ill-formed:
+
* If either {{c|E2}} or {{c|E3}} has type {{c/core|void}}:
:@1.1@ Either {{tt|E2}} or {{tt|E3}} (but not both) is a (possibly parenthesized) {{rlp|throw|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 {{rlp|bit field|bit-field}}, the result is a bit-field. Such conditional operator was commonly used in C++11 {{rlp|constexpr|constexpr programming}} prior to C++14.
+
:* If both {{c|E2}} and {{c|E3}} are of type {{c/core|void}}, the result is a prvalue of type {{c/core|void}}.
 +
:* Otherwise, if the operand of type {{c/core|void}} is a (possibly parenthesized) {{rlp|throw|{{c/core|throw}} expression}}, the result has the type and the value category of the other expression. If the other expression is a {{rlp|bit field|bit-field}}, the result is a bit-field.<ref> Such conditional operator was commonly used in C++11 {{rlp|constexpr|constexpr programming}} prior to C++14.</ref>
 +
:* Otherwise, the program is ill-formed.
 
{{source|1=
 
{{source|1=
 +
2 + 2 == 4 ? throw 123 : throw 456;
 +
 
std::string str = 2 + 2 == 4 ? "OK" : throw std::logic_error("2 + 2 != 4");
 
std::string str = 2 + 2 == 4 ? "OK" : throw std::logic_error("2 + 2 != 4");
}}
 
:@1.2@ Both {{tt|E2}} and {{tt|E3}} are of type {{c|void}} (including the case when they are both throw-expressions). The result is a prvalue of type {{c|void}}.
 
{{source|1=
 
2 + 2 == 4 ? throw 123 : throw 456;
 
 
}}
 
}}
  
@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.
+
* Otherwise, if {{c|E2}} or {{c|E3}} are {{rev inl|until=c++11|lvalue bit-fields}}{{rev inl|since=c++11|glvalue bit-fields of the same value category}} and of types ''cv1'' {{tt|T}} and ''cv2'' {{tt|T}}, respectively, the operands are considered to be of type ''cv'' {{tt|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{{rev inl|since=c++11|, 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.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}};
+
  
 +
* Otherwise, if {{c|E2}} and {{c|E3}} have different types, at least one of which is a (possibly cv-qualified) class type, or both are {{rev inl|until=c++11|lvalues}}{{rev inl|since=c++11|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}}.<ref>Member access{{rev inl|since=c++11|, whether a conversion function is deleted}} and whether an operand is a bit-field are ignored.</ref>
 +
: Attempts are made to form an implicit conversion sequence from an operand expression {{c|X}} of type {{tt|TX}} to a ''target type'' related to the type {{tt|TY}} of the operand expression {{c|Y}} as follows:
 +
:* If {{c|Y}} is an lvalue, the target type is {{tt|TY&}}, but an implicit conversion sequence can only be formed if the reference would {{rlp|reference initialization#Direct binding|bind directly}} to {{rev inl|until=c++11|an lvalue}}{{rev inl|since=c++11|a glvalue}}.
 
{{rrev|since=c++11|
 
{{rrev|since=c++11|
:@3.2@ If {{tt|Y}} is an xvalue, the target type is {{tt|TY&&}}, and the reference must bind directly;
+
:* If {{c|Y}} is an xvalue, the target type is {{tt|TY&&}}, but an implicit conversion sequence can only be formed if the reference would bind directly.
 
}}
 
}}
:@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,
+
:* If {{c|Y}} is {{rev inl|until=c++11|an rvalue}}{{rev inl|since=c++11|a prvalue}} or if none of the conversion sequences above 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}},
+
::* If {{tt|TX}} and {{tt|TY}} are the same class type (ignoring cv-qualification):
::@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}}:
+
:::* If {{tt|TY}} is at least as cv-qualified as {{tt|TX}}, the target type is {{tt|TY}}.
 +
:::* Otherwise, no conversion sequence is formed.
 +
::* Otherwise, if {{tt|TY}} is a base class of {{tt|TX}}, the target type is {{tt|TY}} with the cv-qualifiers of {{tt|TX}}.
 +
::* Otherwise, the target type is the type of {{c|Z}}, where {{c|Z}} is the value of {{c|Y}} after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer {{rlp|implicit conversion|standard conversions}}.
 +
:* Otherwise, no conversion sequence is formed.
 +
: Using this process, it is determined whether an implicit conversion sequence can be formed from {{c|E2}} to the target type determined for the {{c|E3}}, and vice versa.
 +
:* If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.
 +
:* If no conversion sequence can be formed, the operands are left unchanged.
 +
:* Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remaining process.
 
{{source|1=
 
{{source|1=
 
struct A {};
 
struct A {};
Line 192: Line 229:
 
A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B, Target = const A
 
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.4@ If both sequences can be formed ({{tt|E2}} to target type of {{tt|E3}} and {{tt|E3}} to target type of {{tt|E2}}), or only one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.
+
* If {{c|E2}} and {{c|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 {{c|E2}} and {{c|E3}} is a bit-field.
:@3.5@ If exactly one conversion sequence can be formed (note that it may still be ill-formed e.g. due to access violation), that conversion sequence is applied and the converted operand is used in place of the original operand for the remained of this description (starting at {{v|4}})
+
 
:@3.6@ If no conversion sequence can be formed, the operands are left unchanged for the remainder of this description
+
* Otherwise, the result is a prvalue.
@4@ If {{tt|E2}} and {{tt|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 {{tt|E2}} and {{tt|E3}} is a bit-field.
+
:* If {{c|E2}} and {{c|E3}} do not have the same type, and either has (possibly cv-qualified) class type, {{rlp|overload resolution}} is performed using the [[#Overloads|built-in candidates]] to attempt to convert the operands to built-in types.
@5@ Otherwise, the result is a prvalue. If {{tt|E2}} and {{tt|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.
+
::* If the overload resolution fails, the program is ill-formed.
@6@ The lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions are applied to the second and third operands. Then,  
+
::* Otherwise, the selected conversions are applied and the converted operands are used in place of the original operands for the remaining process.
:@6.1@ If both {{tt|E2}} and {{tt|E3}} now have the same type, the result is a prvalue of that type {{rev inl|until=c++17|designating a temporary object}}{{rev inl|since=c++17|whose result object is}} copy-initialized from whatever operand was selected after evaluating {{tt|E1}}.
+
:* The lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions are applied to (possibly-converted) {{c|E2}} and {{c|E3}}. After those conversions, at least one of the following conditions must hold, otherwise the program is ill-formed:
:@6.2@ If both {{tt|E2}} and {{tt|E3}} have arithmetic or enumeration type: the {{rlp|operator arithmetic#conversions|''usual arithmetic conversions''}} are applied to bring them to ''common type'', and that type is the result.
+
::* {{c|E2}} and {{c|E3}} have the same type. In this case, the result is a prvalue of that type {{rev inl|until=c++17|designating a temporary object}}{{rev inl|since=c++17|whose result object is}} copy-initialized from whatever operand was selected after evaluating {{c|E1}}.
:@6.3@ If both {{tt|E2}} and {{tt|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.
+
::* Both {{c|E2}} and {{c|E3}} have arithmetic or enumeration type. In this case, {{rlp|usual arithmetic conversions}} are applied to bring them to their common type, and the result is of that type.
 +
::* Both {{c|E2}} and {{c|E3}} are pointers, or one is a pointer and the other is a {{rlp|pointer#Null pointers|null pointer constant}}, then pointer conversions and qualification conversions are applied to bring them to their {{rlpsd|pointer#Composite pointer type}}, and the result is of that type.
 +
::* If both {{c|E2}} and {{c|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 composite pointer type, and the result is of that type.
 +
{{rrev|since=c++11|
 +
::* If both {{c|E2}} and {{c|E3}} are null pointer constants, and at least one of which is of type {{lc|std::nullptr_t}}, then the result type is {{lc|std::nullptr_t}}.
 +
}}
 
{{source|1=
 
{{source|1=
 
int* intPtr;
 
int* intPtr;
Line 207: Line 249:
 
using Mixed = decltype(true ? nullptr : intPtr);
 
using Mixed = decltype(true ? nullptr : intPtr);
  
static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int*  
+
static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int*
}}
+
 
:@6.4@ If both {{tt|E2}} and {{tt|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.
+
{{source|1=
+
 
struct A
 
struct A
 
{
 
{
Line 224: Line 264:
 
static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
 
static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
 
}}
 
}}
:@6.5@ If both {{tt|E2}} and {{tt|E3}} are null pointer constants, and at least one of which is of type {{lc|std::nullptr_t}}, then the result's type is {{tt|std::nullptr_t}}.
+
 
:@6.6@ In all other cases, the program is ill-formed.
+
<references/>
  
 
{{todo|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}}
 
{{todo|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 {{tt|L}} and {{tt|R}} and for every type {{tt|P}}, where {{tt|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:
+
====Overloads====
 +
For every pair of promoted arithmetic types {{tt|L}} and {{tt|R}} and for every type {{tt|P}}, where {{tt|P}} is a pointer, pointer-to-member, or scoped enumeration type, the following function signatures participate in overload resolution:
 
{{dcl begin}}
 
{{dcl begin}}
 
{{dcl|LR operator?:(bool, L, R);}}
 
{{dcl|LR operator?:(bool, L, R);}}
 
{{dcl|P operator?:(bool, P, P);}}
 
{{dcl|P operator?:(bool, P, P);}}
 
{{dcl end}}
 
{{dcl end}}
where LR is the result of {{rlp|operator arithmetic|usual arithmetic conversions}} performed on {{tt|L}} and {{tt|R}}. The operator “{{tt|?:}}” cannot be overloaded, these function signatures only exist for the purpose of overload resolution.
+
where LR is the result of {{rlp|usual arithmetic conversions}} performed on {{tt|L}} and {{tt|R}}.
  
The return type of a conditional operator is also accessible as the binary type trait {{lc|std::common_type}}.
+
The operator “{{tt|?:}}” cannot be overloaded, these function signatures only exist for the purpose of overload resolution.
 +
 
 +
{{rrev|since=c++11|
 +
The result type of a conditional operator is also accessible as the binary type trait {{lc|std::common_type}}.
 +
}}
  
 
{{example
 
{{example
|
 
 
|code=
 
|code=
#include <string>
 
 
#include <iostream>
 
#include <iostream>
 +
#include <string>
  
 
struct Node
 
struct Node
Line 251: Line 295:
 
     // deep-copying copy constructor
 
     // deep-copying copy constructor
 
     Node(const Node& other)
 
     Node(const Node& other)
      : next(other.next ? new Node(*other.next) : NULL)
+
        : next(other.next ? new Node(*other.next) : NULL)
      , data(other.data)
+
        , data(other.data)
 
     {}
 
     {}
 
      
 
      
 
     Node(int d) : next(NULL), data(d) {}
 
     Node(int d) : next(NULL), data(d) {}
 
      
 
      
     ~Node() { delete next ; }
+
     ~Node() { delete next; }
 
};
 
};
  
Line 303: Line 347:
 
{{dsc inc|cpp/utility/functional/reference_wrapper/dsc operator()}}
 
{{dsc inc|cpp/utility/functional/reference_wrapper/dsc operator()}}
 
{{dsc inc|cpp/utility/functional/function/dsc operator()}}
 
{{dsc inc|cpp/utility/functional/function/dsc operator()}}
 +
{{dsc inc|cpp/utility/functional/move_only_function/dsc operator()}}
 +
{{dsc inc|cpp/utility/functional/copyable_function/dsc operator()}}
 +
{{dsc inc|cpp/coroutine/coroutine_handle/dsc operator()}}
 
{{dsc inc|cpp/locale/locale/dsc operator()}}
 
{{dsc inc|cpp/locale/locale/dsc operator()}}
 
{{dsc inc|cpp/container/value_compare/dsc operator()|map}}
 
{{dsc inc|cpp/container/value_compare/dsc operator()|map}}
 
{{dsc inc|cpp/container/value_compare/dsc operator()|multimap}}
 
{{dsc inc|cpp/container/value_compare/dsc operator()|multimap}}
 
{{dsc inc|cpp/thread/packaged_task/dsc operator()}}
 
{{dsc inc|cpp/thread/packaged_task/dsc operator()}}
{{dsc inc|cpp/numeric/random/engine/dsc operator()|linear_congruential_engine}}<!-- and all other engines -->
+
{{dsc inc|cpp/numeric/random/engine/dsc operator()|linear_congruential_engine}}<!--and all other engines-->
{{dsc inc|cpp/numeric/random/distribution/dsc operator()|uniform_int_distribution}}<!-- and all other distributions -->
+
{{dsc inc|cpp/numeric/random/distribution/dsc operator()|uniform_int_distribution}}<!--and all other distributions-->
 
{{dsc end}}
 
{{dsc end}}
  
The comma operator is not overloaded by any class in the standard library. The boost library uses {{c|operator,}} in [http://www.boost.org/doc/libs/release/libs/assign/doc/index.html#intro boost.assign], boost.spirit, and other libraries. The database access library [http://soci.sourceforge.net/doc.html SOCI] also overloads {{c|operator,}}.
+
The comma operator is not overloaded by any class in the standard library. The boost library uses {{c|operator,}} in [https://www.boost.org/doc/libs/release/libs/assign/doc/index.html#intro boost.assign], [https://www.boost.org/doc/libs/release/libs/spirit/doc/html/index.html boost.spirit], and other libraries. The database access library [https://soci.sourceforge.net/doc.html SOCI] also overloads {{c|operator,}}.
  
 
===Defect reports===
 
===Defect reports===
 
{{dr list begin}}
 
{{dr list begin}}
<!-- the resolution of CWG 324 changed the semantics of operations having bit-field operands, the DR item is noted in the 'bit-field' page -->
+
<!--the resolution of CWG 324 changed the semantics of operators having bit-field operands, the DR item is noted in the 'bit-field' page-->
 
{{dr list item|wg=cwg|dr=446|std=C++98|before=it was unspecified whether a temporary is created for an<br>lvalue-to-rvalue conversion on the conditional operator|after=always creates a temporary if<br>the operator returns a class rvalue}}
 
{{dr list item|wg=cwg|dr=446|std=C++98|before=it was unspecified whether a temporary is created for an<br>lvalue-to-rvalue conversion on the conditional operator|after=always creates a temporary if<br>the operator returns a class rvalue}}
{{dr list item|wg=cwg|dr=462|std=C++98|before=if the second operand of a comma expression is a temporary,<br>it was unspecified whether its lifetime will be extended when<br>the result of the comma expression is bound to a reference|after=the result of the comma expression<br>is the temporary in this case<br>(hence its lifetime is extended)}}
+
{{dr list item|wg=cwg|dr=462|std=C++98|before=if the second operand of a comma operator is a temporary,<br>it was unspecified whether its lifetime will be extended when<br>the result of the comma expression is bound to a reference|after=the result of the comma expression<br>is the temporary in this case<br>(hence its lifetime is extended)}}
{{dr list item|wg=cwg|dr=587|std=C++98|before=when the second and third operands of a conditional<br>expression are lvalues of the same type except for<br>cv-qualification, the result was an lvalue if these<br>operands have class types or an rvalue otherwise|after=the result is always<br>an lvalue in this case}}
+
{{dr list item|wg=cwg|dr=587|std=C++98|before=when the second and third operands of a conditional<br>operator are lvalues of the same type except for<br>cv-qualification, the result was an lvalue if these<br>operands have class types or an rvalue otherwise|after=the result is always<br>an lvalue in this case}}
{{dr list item|wg=cwg|dr=1029|std=C++98|before=the type of a destructor call was unspecified|after=specified as {{c|void}}}}
+
{{dr list item|wg=cwg|dr=1029|std=C++98|before=the type of a destructor call was unspecified|after=specified as {{c/core|void}}}}
{{dr list item|wg=cwg|dr=1550|std=C++98|before=parenthesized throw-expression were not allowed in<br>conditional expressions if other operand is non-void|after=accepted}}
+
{{dr list item|wg=cwg|dr=1550|std=C++98|before=parenthesized {{c/core|throw}} expressions were not allowed in<br>conditional expressions if other operand is non-{{c/core|void}}|after=accepted}}
{{dr list item|wg=cwg|dr=1560|std=C++98|before=void operand in conditional expressions caused<br>gratuitous lvalue-to-rvalue conversion on the<br>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={{c/core|void}} operand of conditional operators caused<br>gratuitous lvalue-to-rvalue conversion on the<br>other operand, always resulting in rvalue|after=a conditional expression<br>with a {{c/core|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=1642|std=C++98|before=the expression {{spar|function}} in a function call<br>expression could be a function pointer lvalue|after=not allowed}}
 +
{{dr list item|wg=cwg|dr=1805|std=C++98|before=when determining the target type for the implicit conversion<br>sequence, the way to convert {{c|Y}} to {{c|Z}} was unclear|after=made clear}}
 
{{dr list item|wg=cwg|dr=1895|std=C++98<br>C++11|before=unclear if deleted (C++11) or inaccessible (C++98)<br>conversion function prevents conversion in<br>conditional expressions, and conversions from base<br>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++98<br>C++11|before=unclear if deleted (C++11) or inaccessible (C++98)<br>conversion function prevents conversion in<br>conditional expressions, and conversions from base<br>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=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=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 item|wg=cwg|dr=2226|std=C++11|before=when determining the target type of the other<br>operand of a conditional operator, reference could<br>not bind to an xvalue if that operand is an lvalue|after=allowed}}
 +
{{dr list item|wg=cwg|dr=2321|std=C++98|before=when determining the target type of the other operand<br>of a conditional operator, a derived class type could<br>not be converted to a less cv-qualified base class type|after=allowed to convert to the base<br>class type with the cv-qualification<br>from the derived class operand}}
 +
{{dr list item|wg=cwg|dr=2715|std=C++98|before=the initialization and destruction of each<br>parameter would occur within the context of<br>the calling function, which might not exist<ref>For example, functions can be called in the initializer of a namespace-scope variable, there is no “calling function” in this context.</ref>|after=occurs within the context of<br>the enclosing full-expression}}
 +
{{dr list item|wg=cwg|dr=2850|std=C++98|before=the destruction order of parameters was unclear|after=made clear}}
 +
{{dr list item|wg=cwg|dr=2865|std=C++98|before=if {{tt|TX}} and {{tt|TY}} are the same class type and {{tt|TX}} is<br>more cv-qualified than {{tt|TY}}, an implicit conversion<br>sequence could still be formed from a prvalue {{c|Y}}|after=no conversion sequence<br>will be formed in this case}}
 
{{dr list end}}
 
{{dr list end}}
 +
<references/>
  
 
===See also===
 
===See also===
{{rlp|operator precedence|Operator precedence}}
+
{{rlp|operator precedence|Operator precedence}}<br>
 
{{rlp|operators|Operator overloading}}
 
{{rlp|operators|Operator overloading}}
  

Latest revision as of 02:13, 8 August 2024

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 
Operator
name
Syntax Over​load​able 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

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.

Contents

[edit] Built-in function call operator

Function call expressions have the following form:

function (arg1, arg2, arg3,...)
function - an expression function type or function pointer type
arg1, arg2, arg3,... - a possibly empty list of arbitrary expressions or brace-enclosed initializer lists(since C++11), except the comma operator is not allowed at the top level to avoid ambiguity

For a call to a non-member function or to a static member function, function can be an lvalue that refers to a function (in which case the function-to-pointer conversion is suppressed), or a prvalue of function pointer type.

The function (or member) name specified by function can be overloaded, overload resolution rules used to decide which overload is to be called.

If function 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 function as well as all expressions arg1, arg2, arg3, etc, provided as arguments are evaluated in arbitrary order, unsequenced with respect to each other.

(until C++17)

function is sequenced before each of arg1, arg2, arg3 as well as default arguments, if any. The argument expressions are evaluated in arbitrary order, indeterminately sequenced with respect to each other.

(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 full-expression where the function call appears, which means, for example, that if a constructor or destructor of a parameter throws an exception, the function try blocks of the called function 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 a parameter is destroyed when the function in which it is defined exits or at the end of the enclosing full-expression. Parameters are always destroyed in the reverse order of their construction.

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 is 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 function specifies a destructor, the return type is void.

When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object.

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

[edit] Built-in comma operator

Comma expressions have the following 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 when using a comma expression as the subscript, e.g., a[(b, c)].

(since C++23)
#include <iostream>
 
int main()
{
    // comma is often used to execute more than one expression
    // where the language grammar allows only one expression:
 
    // * in the third component of the for loop
    for (int i = 0, j = 10; i <= j; ++i, --j)
    //            ^list separator      ^comma operator
        std::cout << "i = " << i << " j = " << j << '\n';
 
    // * in a return statement
    // return log("an error!"), -1;
 
    // * in an initializer expression
    // MyClass(const Arg& arg)
    // : member{ throws_if_bad(arg), arg }
 
    // etc.
 
    // comma operators can be chained; the result of the last
    // (rightmost) expression is the result of the whole chain:
    int n = 1;
    int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n);
 
    // m is now 6
    std::cout << "m = " << (++m, m) << '\n';
}

Output:

i = 0 j = 10
i = 1 j = 9
i = 2 j = 8
i = 3 j = 7
i = 4 j = 6
i = 5 j = 5
n = 2
m = 7

[edit] 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:

  • If either E2 or E3 has type void:
  • If both E2 and E3 are of type void, the result is a prvalue of type void.
  • Otherwise, if the operand of type void is a (possibly parenthesized) throw expression, the result 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.[1]
  • Otherwise, the program is ill-formed.
2 + 2 == 4 ? throw 123 : throw 456;
 
std::string str = 2 + 2 == 4 ? "OK" : throw std::logic_error("2 + 2 != 4");
  • Otherwise, if E2 or E3 are lvalue bit-fields(until C++11)glvalue bit-fields of the same value category(since C++11) 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.
  • Otherwise, if E2 and E3 have different types, at least one of which is a (possibly cv-qualified) class type, or both are lvalues(until C++11)glvalues of the same value category(since C++11) and have the same type except for cv-qualification, then an attempt is made to form an implicit conversion sequence.[2]
Attempts are made to form an implicit conversion sequence from an operand expression X of type TX to a target type related to the type TY of the operand expression Y as follows:
  • If Y is an lvalue, the target type is TY&, but an implicit conversion sequence can only be formed if the reference would bind directly to an lvalue(until C++11)a glvalue(since C++11).
  • If Y is an xvalue, the target type is TY&&, but an implicit conversion sequence can only be formed if the reference would bind directly.
(since C++11)
  • If Y is an rvalue(until C++11)a prvalue(since C++11) or if none of the conversion sequences above can be formed, and at least one of TX and TY is a (possibly cv-qualified) class type:
  • If TX and TY are the same class type (ignoring cv-qualification):
  • If TY is at least as cv-qualified as TX, the target type is TY.
  • Otherwise, no conversion sequence is formed.
  • Otherwise, if TY is a base class of TX, the target type is TY with the cv-qualifiers of TX.
  • Otherwise, the target type is the type of Z, where Z is the value of Y after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions.
  • Otherwise, no conversion sequence is formed.
Using this process, it is determined whether an implicit conversion sequence can be formed from E2 to the target type determined for the E3, and vice versa.
  • If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.
  • If no conversion sequence can be formed, the operands are left unchanged.
  • Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remaining process.
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
  • If 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.
  • Otherwise, the result is a prvalue.
  • If 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 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 the remaining process.
  • The lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions are applied to (possibly-converted) E2 and E3. After those conversions, at least one of the following conditions must hold, otherwise the program is ill-formed:
  • E2 and E3 have the same type. In this case, 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.
  • Both E2 and E3 have arithmetic or enumeration type. In this case, usual arithmetic conversions are applied to bring them to their common type, and the result is of that type.
  • Both 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 their composite pointer type, and the result is of that type.
  • If both 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 composite pointer type, and the result is of that type.
  • If both E2 and E3 are null pointer constants, and at least one of which is of type std::nullptr_t, then the result type is std::nullptr_t.
(since C++11)
int* intPtr;
 
using Mixed = decltype(true ? nullptr : intPtr);
 
static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int*
 
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*>);
  1. Such conditional operator was commonly used in C++11 constexpr programming prior to C++14.
  2. Member access, whether a conversion function is deleted(since C++11) and whether an operand is a bit-field are ignored.

[edit] Overloads

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 overload resolution:

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 result type of a conditional operator is also accessible as the binary type trait std::common_type.

(since C++11)
#include <iostream>
#include <string>
 
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

[edit] 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>) [edit]
returns the sum of two arguments
(public member function of std::plus<T>) [edit]
returns the difference between two arguments
(public member function of std::minus<T>) [edit]
returns the product of two arguments
(public member function of std::multiplies<T>) [edit]
returns the result of the division of the first argument by the second argument
(public member function of std::divides<T>) [edit]
returns the remainder from the division of the first argument by the second argument
(public member function of std::modulus<T>) [edit]
returns the negation of the argument
(public member function of std::negate<T>) [edit]
checks if the arguments are equal
(public member function of std::equal_to<T>) [edit]
checks if the arguments are not equal
(public member function of std::not_equal_to<T>) [edit]
checks if the first argument is greater than the second
(public member function of std::greater<T>) [edit]
checks if the first argument is less than the second
(public member function of std::less<T>) [edit]
checks if the first argument is greater than or equal to the second
(public member function of std::greater_equal<T>) [edit]
checks if the first argument is less than or equal to the second
(public member function of std::less_equal<T>) [edit]
returns the logical AND of the two arguments
(public member function of std::logical_and<T>) [edit]
returns the logical OR of the two arguments
(public member function of std::logical_or<T>) [edit]
returns the logical NOT of the argument
(public member function of std::logical_not<T>) [edit]
returns the result of bitwise AND of two arguments
(public member function of std::bit_and<T>) [edit]
returns the result of bitwise OR of two arguments
(public member function of std::bit_or<T>) [edit]
returns the result of bitwise XOR of two arguments
(public member function of std::bit_xor<T>) [edit]
returns the logical complement of the result of a call to the stored predicate
(public member function of std::unary_negate<Predicate>) [edit]
returns the logical complement of the result of a call to the stored predicate
(public member function of std::binary_negate<Predicate>) [edit]
calls the stored function
(public member function of std::reference_wrapper<T>) [edit]
invokes the target
(public member function of std::function<R(Args...)>) [edit]
invokes the target
(public member function of std::move_only_function) [edit]
invokes the target
(public member function of std::copyable_function) [edit]
resumes execution of the coroutine
(public member function of std::coroutine_handle<Promise>) [edit]
lexicographically compares two strings using this locale's collate facet
(public member function of std::locale) [edit]
compares two values of type value_type
(public member function of std::map<Key,T,Compare,Allocator>::value_compare) [edit]
compares two values of type value_type
(public member function of std::multimap<Key,T,Compare,Allocator>::value_compare) [edit]
executes the function
(public member function of std::packaged_task<R(Args...)>) [edit]
advances the engine's state and returns the generated value
(public member function of std::linear_congruential_engine<UIntType,a,c,m>) [edit]
generates the next random number in the distribution
(public member function of std::uniform_int_distribution<IntType>) [edit]

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,.

[edit] 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 operator 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
operator 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 expressions were not allowed in
conditional expressions if other operand is non-void
accepted
CWG 1560 C++98 void operand of conditional operators caused
gratuitous lvalue-to-rvalue conversion on the
other operand, always resulting in rvalue
a conditional expression
with a void can be lvalue
CWG 1642 C++98 the expression function in a function call
expression could be a function pointer lvalue
not allowed
CWG 1805 C++98 when determining the target type for the implicit conversion
sequence, the way to convert Y to Z was unclear
made clear
CWG 1895 C++98
C++11
unclear if deleted (C++11) or inaccessible (C++98)
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 of a conditional operator, reference could
not bind to an xvalue if that operand is an lvalue
allowed
CWG 2321 C++98 when determining the target type of the other operand
of a conditional operator, a derived class type could
not be converted to a less cv-qualified base class type
allowed to convert to the base
class type with the cv-qualification
from the derived class operand
CWG 2715 C++98 the initialization and destruction of each
parameter would occur within the context of
the calling function, which might not exist[1]
occurs within the context of
the enclosing full-expression
CWG 2850 C++98 the destruction order of parameters was unclear made clear
CWG 2865 C++98 if TX and TY are the same class type and TX is
more cv-qualified than TY, an implicit conversion
sequence could still be formed from a prvalue Y
no conversion sequence
will be formed in this case
  1. For example, functions can be called in the initializer of a namespace-scope variable, there is no “calling function” in this context.

[edit] See also

Operator precedence
Operator overloading

Common operators
assignment increment
decrement
arithmetic logical comparison member
access
other

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b

a[...]
*a
&a
a->b
a.b
a->*b
a.*b

function call
a(...)
comma
a, b
conditional
a ? b : c
Special operators

static_cast converts one type to another related type
dynamic_cast converts within inheritance hierarchies
const_cast adds or removes cv-qualifiers
reinterpret_cast converts type to unrelated type
C-style cast converts one type to another by a mix of static_cast, const_cast, and reinterpret_cast
new creates objects with dynamic storage duration
delete destructs objects previously created by the new expression and releases obtained memory area
sizeof queries the size of a type
sizeof... queries the size of a parameter pack (since C++11)
typeid queries the type information of a type
noexcept checks if an expression can throw an exception (since C++11)
alignof queries alignment requirements of a type (since C++11)

C documentation for Other operators