0003 - Simplified Overload Resolution
| Status | Refinement |
|---|---|
| Author | |
| Sponsor |
Implementation Status
| DXC | Clang | |
|---|---|---|
| C++-aligned best match | Not Planned | Complete |
| C++-aligned candidate selection | Not Planned | Complete |
Introduction
This proposal suggests adopting an overload resolution algorithm that aligns with C++’s approach to overload resolution. This algorithm will provide more intuitive behavior for users and will bias toward erroring instead of choosing an overload from ambiguous candidate sets.
Motivation
DXC’s overload resolution is near impossible to match completely without inheriting significant implementation details of the compiler. Notabily, the way DXC selects possible overload candidates is different for built-in functions than for user-defined functions, which means that a calls to user-defined functions can resolve differently than calls to built-in functions even if the two functions have identical sets of possible overloads.
This situation is made more complicated by HLSL 2021’s introduction of operator overloading which expands the context in which overloads need to be resolved. DXC’s approach to overload resolution has unresolved bugs some of which cannot be fixed without changing the behavior of existing code.
Aligning HLSL’s overload resolution with C++ aids in addressing these issues as well as making our language more future-resistant, and simplifying the implementation in compilers already designed to mimic or support C++. The C++-aligned specification language is also dramatically simplified specification language which will be easier to test for conformance.
Proposed solution
Implicit conversions
This proposal suggests introducing new implicit conversions and conversion ranks to classify conversions involving matrix and vector types as source and/or destination arguments. The table below is the full list of proposed implicit conversions and their ranks. The bolded are unique to HLSL and introduced by this proposal, the remaining elements are inherited from C++. The table below is sorted by the ranks in increasing order such that the conversion of the lowest rank is preferred.
| Conversion | Rank |
|---|---|
| No conversion (Identity) | Exact Match |
| Lvalue-to-rvalue | Exact Match |
| Array-to-pointer | Exact Match |
| Qualification | Exact Match |
| Single Element to Scalar | Exact Match |
| Vector Scalar splat conversion | Extension |
| Matrix Scalar splat conversion | Extension |
| Integral promotion | Promotion |
| Floating point promotion | Promotion |
| Component-wise promotion | Promotion |
| Scalar splat promotion | Promotion Extension |
| Integral conversion | Conversion |
| Floating point conversion | Conversion |
| Floating-integral conversion | Conversion |
| Boolean conversion | Conversion |
| Component-wise conversion | Conversion |
| Scalar splat conversion | Conversion Extension |
| Vector truncation (without conversion) | Truncation |
| Matrix truncation (without conversion) | Truncation |
| Vector truncation promotion | Promotion Truncation |
| Matrix truncation promotion | Promotion Truncation |
| Vector truncation conversion | Conversion Truncation |
| Matrix truncation conversion | Conversion Truncation |
The new conversions allow combining element type conversions with dimension conversions. Supported implicit dimension conversions are either reductions in row or column counts by discarding elements, or scalar extensions by broadcasting a value to fill a matrix or vector.
Additionally a vector or matrix containing only one element may be implicitly converted to a scalar of the element type, and an additional conversion sequence may be aplied from that scalar type.
Overload resolution contexts
In HLSL 2021 as implemented by DXC overload resolution only occurs on the invocation of a function named in a function call expression, for a subset of binary and unary operators referenced when the argument to the left hand side is a class object, or for the function call operator on a class object.
This proposal extends the contexts that overload resolution occurs in to include conversion operators which may be explicitly invoked via C-style casting syntax or implicitly invoked when converting objects of class type to built-in types or during direct and copy initialization.
With this change we also introduce the explicit keyword to HLSL to allow
marking an operator as explicit so that it may not be considered for implicit
conversions.
Overload candidate selection
DXC has different approaches for identification of overload candidates for built-in functions and user-defined functions. This proposal suggests aligning with the C++ candidate selection rules which are currently used for user-defined functions.
Overload resolution algorithm
DXC uses a scoring algorithm where each conversion is assigned a rank, the number of conversions of each rank are summed, and the overload with the fewest conversions of the worst rank is selected. This algorithm disambiguates some cases that would be ambiguous in C++, but matches C++ overload resolution in all non-ambiguous cases.
This proposal adopts a modified overload resolution algorithm from C++ which is updated to account for the new conversions and conversion ranks defined in the implicit conversions section of this document.
Detailed Design
Overloading [Overload]
When a single name is declared with two or more different declarations in the same scope, the name is overloaded. A declaration that declares an overloaded name is called an overloaded declaration. The set of overloaded declarations that declare the same overloaded name are that name’s overload set.
Only function and function template declarations can be overloaded; variable and type declarations cannot be overloaded.
Overloadable Declarations [Overload.Decl]
This section specifies the cases in which a function declaration cannot be overloaded. Any program that contains an invalid overload set is ill-formed.
An overload set is invalid if:
- One or more declaration in the overload set only differ by return type.
int Yeet();
uint Yeet(); // ill-formed: decls differ only by return type
- An overload set contains more than one member function declarations with
the same parameter-type-list, and one of those declarations is a
staticmember function declaration (\ref{Classes.Static}).
class Doggo {
static void pet();
void pet(); // ill-formed: cannot be overloaded with `static void pet()`
static void wagTail();
void wagTail(); // valid
void bark(int);
static void bark(int); // ill-formed: cannot be overloaded with `void bark(int)`
};
- An overload set contains more than one entry function declaration (\ref{Decl.Attr.Entry}).
[shader("vertex")]
void VS();
void VS(int); // valid: only one entry point.
[shader("vertex")]
void Entry();
[shader("compute")]
void Entry(int); // ill-formed: an overload set cannot have more than one entry function
- An overload set contains more than one function declaration which only
differ in
constparameter specifiers.
void G(int);
void G(const int); // ill-formed: redeclaration of G(int)
void G(int) {}
void G(const int) {} // ill-formed: redefinition of G(int)
- An overload set contains more than one function declaration which only
differ in parameters mismatching
outandinout.
void H(int);
void H(in int); // valid: redeclaration of H(int)
void H(inout int); // valid: overloading between in and inout is allowed
void I(in int);
void I(out int); // valid: overloading between in and out is allowed
void J(out int);
void J(inout int); // ill-formed: Cannot overload based on out/inout mismatch
Declaration Matching [Overload.Match]
Two function declarations with the same name and same parameter lists refer to the same function if they are in the same scope (\ref{Basic.Scope}). A function declaration in a containing scope is not the same as a function of the same name and parameter list in a contained scope.
Overload Resolution [Overload.Res]
Overload resolution is process by which a function call is mapped to a the best overloaded function declaration. Overload resolution uses set of functions called the candidate set, and a list of expressions that comprise the argument list for the call.
Overload resolution selects the function to call in the following contexts:
- invocation of a function named in a function call expression;
- invocation of a function call operator on a class object named in function call syntax;
- invocation of the operator referenced in an expression;
- invocation of a user-defined conversion function for copy-initialization of an object of class type; and
- invocation of a user-defined conversion function for initializing an object of non-class type from an expression of class type.
In each of these contexts a unique method is used to construct the overload candidate set and argument expression list.
Candidate Functions and Argument Lists [Overload.Res.Sets]
At each point where an overload must be resolved an implementation will generate a candidate set, containing all possible candidate functions, and the argument list for the overloaded call. The subclauses under this clause define the process by which the candidate set and argument lists are constructed in each context where overload resolution occurs.
A candidate set may include both member and non-member functions to be resolved against the same argument set. Member functions have an extra implicit object parameter, representing the object the function is called on. For the purposes of overload resolution a static member function is considered to have an implicit object parameter.
When appropriate the context shall construct an implied object argument referring to the object operated on.
Any implicit object parameter or implied object argument is always the first parameter or argument.
The type of the implicit object parameter is: lvalue reference cv T; where T
is the class containing the declaration of the function, and cv is the
cv-qualification on the function declaration.
Implied object arguments behave the same as any explicit argument except that no temporary object shall be created to hold the argument for the implicit object parameter except when the implicit object parameter is a member of a cbuffer-declaration.
If a candidate is a function template, candidate specializations are generated through template argument deduction (\ref{Template.Deduction}), candidates are then handled as any other candidate. A name may refer to more than one function template, in which case candidate functions shall be generated for each function template to form the full candidate set.
Function Call Syntax [Overload.Res.Call]
postfix-expression ( \opt{expression-list} )
In a function call (\ref{Expr.Post.Call}), if the postfix-expression names a candidate set comprised of overloaded functions and/or function templates, the overload resolves as to a named function (\ref{Overload.Res.Call.Named}). If the postfix-expression names an object of class type, the overload is resolved as a call to an object of class type (\ref{Overload.Res.Call.Object}).
Call to a Named Function [Overload.Res.Call.Named]
In a call to a named function, the postfix-expression must be one of the forms:
postfix-expression:
postfix-expression . id-expression
primary-expression
In the first grammar production, the id-expression is the name of the function
to be called, and the postfix-expression preceding the . operator. This
formation represents a qualified function call, where the postfix-expression
must be an object of class type, and the call is looked up as a member function
of that object. The function names found by that lookup constitute the overload
candidate set for the call. The argument list for the call is the
expression-list in the call with the left operand of the . operator
prepended as the implicit object parameter (\ref{Overload.Res.Sets}).
In the second grammar production, the name is not qualified by an object
parameter, so it is an unqualified call. Name lookup occurs as normal for name
lookup of a function call (\ref{Basic.Lookup}). Function declarations found
constitute the candidate set. The candidate set for an unqualified call will be
comprised either entirely of non-member functions or entirely of member
functions of some class T. If the candidate set is entirely non-member
functions, the argument list is the expression-list. If the candidate set is
entirely member functions of class T, the argument list is the
expression-list with an implied object argument prepended. If the call
expression occurs in a context where an implicit object this is in scope and
of class type T or a type T\``, the thisobject will be the implicit object argument, otherwise a contrived argument of typeT` will become the implied
object argument. If overload resolution selects a non-static member function,
and the implicit object argument is a contrived argument, the call is
ill-formed.
Call to a Object of Class Type [Overload.Res.Call.Object]
If the primary-expression in the function call expression is a class object of
type T, then the set of candidate functions will include the call operators of
T obtained by name lookup of the name operator() (\ref{Basic.Lookup}).
For this formulation, the argument list consists of the expression-list from the call syntax with the implicit object argument prefixed. The implicit object argument in this form is the object produced from the evaluated postfix-expression.
Operators in Expressions [Overload.Res.Oper]
Operators in an expression where no operands have a class or enumeration type are assumed to be built-in operators and interpreted following \ref{Expr}.
If either operand is of class or enumeration type, overload resolution determines which operator function or built-in operator is resolved for the expression.
The table below maps unary and binary operator expressions to equivalent member
and non-member function call expressions. In the table @ represents any
spellable operator, and a and b represent arguments to the operator.
| Expression | Member function | Non-member function | | @a | (a).operator@() | operator@(a) | | a@b | (a).operator@(b) | operator@(a,b) | | a[b] | (a).operator | |
Given the table above, operand a is of type T1, and operand b is of type
T2. T1 must be of class or enumeration type, and T2 may be of any type
including T1. The candidate set for operator resolution is constructed by the
process:
- If
T1is a class type, the result of qualified lookup ofoperator@is added to the set. - The result of unqualified lookup of
operator@in the context of the expression excluding the results from step 1. If no operand is of class type, only functions whereT1orT2are enumerations are added to the set. - The built-in operators of
operator@from the operators defined in \ref{Overload.Builtin} are added to the set.
The left operand to built-in assignment operators, may not have user-defined conversion applied to it, and no temporary may be introduced to hold it.
If the operator is the , operator, and there are no viable functions, the
built-in operator is used.
Initialization by Conversion [Overload.Res.Convert]
An object of class type or non-class type T may be initialized with an
initializing expression of class type S invoking a user-defined conversion.
The candidate set of conversion functions will be the non-explicit conversion
functions of S and its base classes.
The argument list has one argument which is the initializer expression.
Viable Functions [Overload.Res.Viable]
Given the candidate set and argument expressions as determined by the relevant context (\ref{Overload.Res.Sets}), a subset of viable functions can be selected from the candidate set.
A function candidate $F(P_0 … P_m)$ is not a viable function for a call with argument list $A_0 … A_n$ if:
- The function has fewer parameters than there are arguments in the argument list ($m < n$).
- The function has more parameters than there are arguments to the argument list ($m > n$), and function parameters $ P_{n+1} … P_m $ do not all have default arguments.
- There is not an implicit conversion sequence that converts each argument $A_i$ to the type of the corresponding parameter $P_i$.
Best Viable Function [Overload.Res.Best]
For an overloaded call with arguments $A_0 … A_n$, each viable function $F(P_0 … P_m)$, has a set of implicit conversion sequences $ICS_0(F) … ICS_m(F)$ defining the conversion sequences for each argument $A_i$ to the type of parameter $P_i$.
A viable function $F$ is defined to be a better function than another viable function $f`$ if for all arguments $ICS_i(F)$ is not a worse conversion sequence than $ICS_i(f`)$, and:
- for some argument $j$, $ICS_j(F)$ is a better conversion than $ICS_j(f`)$ or,
- in the context of an initialization by user-defined conversion, the conversion sequence from the return type of $F$ to the destination type is a better conversion sequence than the return type of $f`$ to the destination type or,
- $F$ is a non-template function and $f`$ is a function template specialization, or
- $F$ and $f`$ are both function template specializations and $F$ is more specialized than $f`$ according to function template partial ordering rules (\ref{Template.Func.Order}).
If there is one viable function that is a better function than all the other viable functions, it is the selected function; otherwise the call is ill-formed.
If the resolved overload is a function with multiple declarations, and if at least two of these declarations specify a default argument that made the function viable, the program is ill-formed.
void F(int X = 1);
void F(float Y = 2.0f);
void Fn() {
F(1); // Okay.
F(3.0f); // Okay.
F(); // Ill-formed.
}
Implicit Conversion Sequences [Overload.ICS]
An implicit conversion sequence is a sequence of conversions which converts a source value to a prvalue of destination type. In overload resolution the source value is the argument expression in a function call, and the destination type is the type of the corresponding parameter of the function being called.
When a parameter is a cxvalue an inverted implicit conversion sequence is required to convert the parameter type back to the argument type for writing back to the argument expression lvalue. An inverted implicit conversion sequence must be a well-formed implicit conversion sequence where the source value is the implicit cxvalue of the parameter type, and the destination type is the argument expression’s lvalue type.
A well-formed implicit conversion sequence is either a standard conversion sequence, or a user-defined conversion sequence.
In the following contexts, an implicit conversion sequence can only be a standard conversion sequence:
- Argument conversion for a user-defined conversion function.
- Copying a temporary for class copy-initialization.
- When passing an initializer-list as a single argument.
- Copy-initialization of a class by user-defined conversion.
An implicit conversion sequence models a copy-initialization unless it is an inverted implicit conversion sequence when it models an assignment. Any difference in top-level cv-qualification is handled by the copy-initialization or assignment, and does not constitute a conversion.
Note: “Top-level” cv-qualification refers to the qualification of the value. This means an parameter of type
Tcan be initialized by a argument of typeconst T. This does not mean that a parameter of typeinout Tcan be initialized with a argument of typeconst Tbecause there is no valid inverted conversion system to assign back to a value of typeconst T.
When the source value type and the destination type are the same, the implicit conversion sequence is an identity conversion, which signifies no conversion.
Only standard conversion sequences that do not create temporary objects are valid for implicit object parameters or left operand to assignment operators.
If no sequence of conversions can be found to convert a source value to the destination type, an implicit conversion sequence cannot be formed.
If several different sequences of conversions exist that convert the source value to the destination type, the implicit conversion sequence is defined to be the unique conversion sequence designated the ambiguous conversion sequence. For the purpose of ranking implicit conversion sequences, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other user-defined conversion sequence. If overload resolution selects a function using the ambiguous conversion sequence as the best match for a call, the call is ill-formed.
Standard Conversion Sequences [Overload.ICS.SCS]
The conversions that comprise a standard conversion sequence and the composition of the sequence are defined in Chapter \ref{Conv}.
Each standard conversion is given a category and rank as defined in the table below:
| Conversion | Category | Rank | Reference |
|---|---|---|---|
| No conversion | Identity | Exact Match | |
| Lvalue-to-rvalue | Lvalue Transformation | Exact Match | \ref{Conv.lval} |
| Array-to-pointer | Lvalue Transformation | Exact Match | \ref{Conv.array} |
| Qualification | Qualification Adjustment | Exact Match | \ref{Conv.qual} |
| Vector Scalar splat (without conversion) | Scalar Extension | Extension | \ref{Conv.vsplat} |
| Matrix Scalar splat (without conversion) | Scalar Extension | Extension | \ref{Conv.msplat} |
| Integral promotion | Promotion | Promotion | \ref{Conv.iconv} & \ref{Conv.rank.int} |
| Floating point promotion | Promotion | Promotion | \ref{Conv.fconv} & \ref{Conv.rank.float} |
| Component-wise promotion | Promotion | Promotion | \ref{Conv.cwise} |
| Vector Scalar splat promotion | Scalar Extension Promotion | Promotion Extension | \ref{Conv.vsplat} |
| Matrix Scalar splat promotion | Scalar Extension Promotion | Promotion Extension | \ref{Conv.msplat} |
| Integral conversion | Conversion | Conversion | \ref{Conv.iconv} |
| Floating point conversion | Conversion | Conversion | \ref{Conv.fconv} |
| Floating-integral conversion | Conversion | Conversion | \ref{Conv.fpint} |
| Boolean conversion | Conversion | Conversion | \ref{Conv.bool} |
| Component-wise conversion | Conversion | Conversion | \ref{Conv.cwise} |
| Scalar splat conversion | Scalar Extension Conversion | Conversion Extension | \ref{Conv.vsplat} |
| Vector Scalar splat conversion | Scalar Extension Conversion | Conversion Extension | \ref{Conv.vsplat} |
| Matrix Scalar splat conversion | Scalar Extension Conversion | Conversion Extension | \ref{Conv.msplat} |
| Vector truncation (without conversion) | Dimensionality Reduction | Truncation | \ref{Conv.vtrunc} |
| Matrix truncation (without conversion) | Dimensionality Reduction | Truncation | \ref{Conv.vtrunc} |
| Vector truncation promotion | Dimensionality Reduction Promotion | Promotion Truncation | \ref{Conv.vtrunc} |
| Matrix truncation promotion | Dimensionality Reduction Promotion | Promotion Truncation | \ref{Conv.vtrunc} |
| Vector truncation conversion | Dimensionality Reduction Conversion | Conversion Truncation | \ref{Conv.vtrunc} |
| Matrix truncation conversion | Dimensionality Reduction Conversion | Conversion Truncation | \ref{Conv.vtrunc} |
If a scalar splat conversion occurs in a conversion sequence where all other conversions are Exact Match rank, the conversion is ranked as Extension. If a scalar splat occurs in a conversion sequence with a Promotion conversion, the conversion is ranked as Promotion Extension. If a scalar splat occurs in a conversion sequence with a Conversion conversion, the conversion is ranked as Conversion Extension.
If a vector or matrix truncation conversion occurs in a conversion sequence where all other conversions are Exact Match rank, the conversion is ranked as Truncation. If a vector or matrix truncation occurs in a conversion sequence with a Promotion conversion, the conversion is ranked as Promotion Truncation. If a vector or matrix truncation occurs in a conversion sequence with a Conversion conversion, the conversion is ranked as Conversion Truncation.
Otherwise, the rank of a conversion sequence is determined by considering the rank of each conversion.
Conversion sequence ranks are ordered from better to worse as:
- Exact Match
- Extension
- Promotion
- Promotion Extension
- Conversion
- Conversion Extension
- Truncation
- Promotion Truncation
- Conversion Truncation
User-defined Conversion Sequences
A user-defined conversion sequence is a standard conversion sequence followed by a user-defined conversion, followed by a second standard conversion sequence.
The first standard conversion sequence converts the source type to the type of the object parameter of the conversion function of the user-defined conversion.
The second standard conversion converts the result of the user-defined conversion to the target type for the conversion sequence.
If the user-defined conversion is a template specialization, the second standard conversion sequence shall have the rank Exact Match.
If the user-defined conversion specifies a conversion from a class type to the same class type, it shall have the rank Exact Match even if a conversion function is invoked and it produces temporaries or other side-effects.
If the user-defined conversion converts from a class type to a base class of that type, it shall have the rank Conversion even if a conversion function is invoked and it produces temporaries or other side-effects.
Comparing Implicit Conversion Sequences [Overload.ICS.Comparing]
A partial ordering of implicit conversion sequences exists based on defining relationships for better conversion sequence, and better conversion. If an implicit conversion sequence $ICS(f)$ is a better conversion sequence than $ICS(f`)$, then the inverse is also true: $ICS(f`)$ is a worse conversion sequence than $ICS(f)$. If $ICS(f)$ is neither better nor worse than $ICS(f`)$, the conversion sequences are indistinguishable conversion sequences.
A standard conversion sequence is always better than a user-defined conversion sequence.
Standard conversion sequences are ordered by their ranks. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:
- If class
Bis derived directly or indirectly from classAand classCis derived directly or indirectly from classB,- binding of a expression of type
Cto a cxvalue of typeBis better than binding an expression of typeCto a cxvalue of typeA, - conversion of
CtoBis better than conversion orCtoA, - binding of a expression of type
Bto a cxvalue of typeAis better than binding an expression of typeCto a cxvalue of typeA, - conversion of
BtoAis better than conversion ofCtoA.
- binding of a expression of type
Overloaded Operators [Overload.Operators]
operator-function-id:
`operator` operator
operator: one of
`+` `-` `*` `/` `%` `^` `&` `|` `~` `!` `<` `>` `<<` `>>` `<=` `>=` `&&` `||` `,` `()` `[]`
The operators +, -, *, and & may only be overloaded in their binary
form.
The ., :: and ?: operators cannot be overloaded.
Operator functions may be called directly by their name in the form operator@
or through the resolution of an overload from an operator expression.
An overloaded operator function must be either a non-static member function or a non-member function, and must have at least one parameter whose type is a class or enumeration.
An overloaded operator may not change the number of operands of an operator, except the function call operator which may have any number of operands.
The meaning of the , operator, which is pre-defined for all types, can be
changed by operator overloading.
Overloaded operator functions are inherited from base classes in the same way as other base class member functions.
Only an overloaded call operator may have default arguments, no other operator functions may be defined with default arguments. All operator functions must take exactly the number of parameters of the corresponding operator as defined in this subclause.
Unary Operators
An overloaded prefix unary operator must be implemented by a non-static member function with no parameters or a non-member function with one parameter.
Postfix unary operators are not overloadable.
Binary Operators
An overloaded binary operator must be implemented by a non-static member function with one parameter or a non-member function with two parameters.
Function Call Operator
An overloaded function call operator must be a non-static member function with an arbitrary number of parameters, and may have default arguments. Function call operator functions are overload candidates for the function call syntax when the postfix-expression is an object of class type.
Subscript operator
An overloaded subscript operator must be a non-static member function with one parameter. Subscript operator functions are overload candidates for the subscript syntax when the postfix-expression is an object of class type.
Built-in Operators [Overload.Builtin]
For binary operators on arithmetic types, the result type is called the promoted expression type as determined by usual arithmetic conversions (\ref{Expr.Conv}).
For all types T where T is an arithmetic type (\ref{Basic.Types.Arithmetic});
operators will be defined of the forms:
T operator+(T );
T operator-(T );
T operator~(T );
For all types L, R and LR, where L and R are any arithmetic type
(\ref{Basic.Types.Arithmetic}), and LR is the promoted expression type;
operators will be defined of the forms:
LR operator*(L , R );
LR operator/(L , R );
LR operator+(L , R );
LR operator-(L , R );
bool operator<(L , R );
bool operator>(L , R );
bool operator<=(L , R );
bool operator>=(L , R );
bool operator==(L , R );
bool operator!=(L , R );
LR operator%(L , R );
For all types T where T is an enumeration type (\ref{Decl.Enum}); operators
will be defined of the form:
bool operator<(T , T );
bool operator>(T , T );
bool operator<=(T , T );
bool operator>=(T , T );
bool operator==(T , T );
bool operator!=(T , T );
For all types T where T is an integral type
(\ref{Basic.Types.Arithmetic}); operators will be defined of the form:
LR operator&(L , R );
LR operator^(L , R );
LR operator|(L , R );
L operator<<(L , R );
L operator>>(L , R );
For all types L and R where L and R are arithmetic types
(\ref{Basic.Types.Arithmetic}), and CVQ is a cv-qualifier; operators will be
defined of the form:
CVQ L & operator=(CVQ L &, R );
CVQ L & operator*=(CVQ L &, R );
CVQ L & operator/=(CVQ L &, R );
CVQ L & operator+=(CVQ L &, R );
CVQ L & operator-=(CVQ L &, R );
CVQ L & operator%=(CVQ L &, R );
For all types L and R where L and R are integral types
(\ref{Basic.Types.Arithmetic}), and CVQ is a cv-qualifier; operators will be
defined of the form:
CVQ L & operator<<=(CVQ L &, R );
CVQ L & operator>>=(CVQ L &, R );
CVQ L & operator&=(CVQ L &, R );
CVQ L & operator^=(CVQ L &, R );
CVQ L & operator|=(CVQ L &, R );
For the boolean type bool operators will be defined of the form:
bool operator!(bool);
bool operator&&(bool, bool);
bool operator||(bool, bool);
For all types L, R and LR, where L and R are any arithmetic type
(\ref{Basic.Types.Arithmetic}), and LR is the promoted expression type;
the ternary operator will be defined of the form:
LR operator?:(bool, L , R );
For all types T, a ternary operator will be defined of the form:
T operator?:(bool, T , T );