This code:
#include <iostream>
template <typename T>
void print_type(){ std::cout << __PRETTY_FUNCTION__ << '\n'; }
template <typename T>
struct foo {
operator T(){
std::cout << "T conversion ";
print_type<T>();
return {};
}
template <typename S>
operator S(){
std::cout << "ANY conversion ";
print_type<S>();
return {};
}
};
int main(void) {
unsigned a = 20;
foo<uint8_t> z;
auto y = z*a;
}
compiles (with gcc 9.1.0) and prints:
ANY conversion void print_type() [with T = int]
On the other hand, if I remove the operator T
(which is not called above):
template <typename T>
struct bar {
template <typename S>
operator S(){
std::cout << "ANY conversion ";
print_type<S>();
return {};
}
};
int main(void) {
unsigned a = 20;
bar<uint8_t> z;
auto y = z*a;
}
I get an error:
prog.cc: In function 'int main()':
prog.cc:19:15: error: no match for 'operator*' (operand types are 'bar<unsigned char>' and 'unsigned int')
19 | auto y = z*a;
| ~^~
| | |
| | unsigned int
| bar<unsigned char>
At first I was surprised that foo
requires an operator T
for operator S
to be choosen. However, is gcc even right here? Clang 8.0 complains with
prog.cc:24:15: error: use of overloaded operator '*' is ambiguous (with operand types 'foo<uint8_t>' (aka 'foo<unsigned char>') and 'unsigned int')
auto y = z*a;
~^~
prog.cc:24:15: note: built-in candidate operator*(float, unsigned int)
prog.cc:24:15: note: built-in candidate operator*(double, unsigned int)
prog.cc:24:15: note: built-in candidate operator*(long double, unsigned int)
prog.cc:24:15: note: built-in candidate operator*(__float128, unsigned int)
[...]
... the list continues with all kinds of candidates.
Why does the first example compile with gcc but not with clang? Is this a bug in gcc?
This is a real tour-de-force of the standard.
When foo<uint8_t>
is instantiated, the specialization looks like this:
struct foo<uint8_t> {
operator uint8_t(){
std::cout << "T conversion ";
print_type<uint8_t>();
return {};
}
template <typename S>
operator S(){
std::cout << "ANY conversion ";
print_type<S>();
return {};
}
};
In other words, the class contains a non-template conversion operator to uint8_t
, and a conversion operator template to arbitrary S
.
When the compiler sees z * a
, [over.match.oper]/(3.3) defines the set of built-in candidates:
For the operator
,
, the unary operator&
, or the operator->
, the built-in candidates set is empty. For all other operators, the built-in candidates include all of the candidate operator functions defined in 16.6 that, compared to the given operator, * have the same operator name, and * accept the same number of operands, and * accept operand types to which the given operand or operands can be converted according to 16.3.3.1, and * do not have the same parameter-type-list as any non-member candidate that is not a function template specialization.
The built-in candidates defined in 16.6/13 for operator*
are:
For every pair of promoted arithmetic types
L
andR
, there exist candidate operator functions of the formLR operator*(L, R); // ...
Clang is printing out the full list of such built-in candidates. Presumably GCC agrees with this list. Now overload resolution must be applied to select the one to "call". (Of course, the built-in operator*
is not a real function, so "calling" it just means converting the arguments to the "parameter" types and then executing the built-in multiplication operator.) Obviously, the best viable candidate would have R be unsigned int
so that we get an exact match for the second argument, but what about the first argument?
For a given L, the compiler must recursively apply overload resolution with the candidates described in [over.match.conv] to determine how to convert foo<uint8_t>
to L:
Under the conditions specified in 11.6, as part of an initialization of an object of non-class type, a conversion function can be invoked to convert an initializer expression of class type to the type of the object being initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1
T
” is the type of the object being initialized, and “cvS
” is the type of the initializer expression, withS
a class type, the candidate functions are selected as follows:
- The conversion functions of
S
and its base classes are considered. Those non-explicit conversion functions that are not hidden withinS
and yield typeT
or a type that can be converted to typeT
via a standard conversion sequence (16.3.3.1.1) are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden withinS
and yield typeT
or a type that can be converted to typeT
with a qualification conversion (7.5) are also candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. Conversion functions that return “reference to cv2X
” return lvalues or xvalues, depending on the type of reference, of type “cv2X
” and are therefore considered to yieldX
for this process of selecting candidate functions.The argument list has one argument, which is the initializer expression. [ Note: This argument will be compared against the implicit object parameter of the conversion functions. —end note ]
So one candidate to convert foo<uint8_t>
to L is to call operator uint8_t
and then do whatever standard conversion is necessary to convert uint8_t
to L. The other candidate is to call operator S
, but S
must be deduced as specified in [temp.deduct.conv]:
Template argument deduction is done by comparing the return type of the conversion function template (call it
P
) with the type that is required as the result of the conversion (call itA
; see 11.6, 16.3.1.5, and 16.3.1.6 for the determination of that type) as described in 17.8.2.5. ...
Thus, the compiler will deduce S
= L.
To select whether to call operator uint8_t
or operator
L, the overload resolution process is used with the foo<uint8_t>
object as the implied object argument. Since the conversion from foo<uint8_t>
to the implied object argument type is just the identity conversion in both cases (as both operators are direct members with no cv-qualification), the tie-breaker rule [over.match.best]/(1.4) must be used:
the context is an initialization by user-defined conversion (see 11.6, 16.3.1.5, and 16.3.1.6) and the standard conversion sequence from the return type of
F1
to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type ofF2
to the destination type ...
Thus, the compiler will always select operator
L over operator uint8_t
in order to get the identity conversion from the result of the conversion operator to L (unless L itself is uint8_t
, but that can't happen because L must be a promoted type).
Thus, for each possible L, to "call" operator* LR(L, R)
, the implicit conversion sequence required for the first argument is the user-defined conversion of calling operator
L. When comparing operator*
s with different L's, there is no way for the compiler to decide which one is best: in other words, should it call operator int
to call operator*(int, unsigned int)
, or should it call operator unsigned int
to call operator*(unsigned int, unsigned int)
, or should it call operator double
to call operator*(double, unsigned int)
, and so on? All are equally good options, and the overload is ambiguous. Thus, Clang is right and GCC has a bug.
Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.
En caso de infracción, por favor [email protected] Eliminar
Déjame decir algunas palabras