Date: Wed, 30 Dec 2020 13:48:17 +0800
For your first example, the parameter of the first function is of type
`int` and the corresponding argument has type `S`, Hence, the following
rules apply here:
> Otherwise, if the source type is a (possibly cv-qualified) class type,
conversion functions are considered. The applicable conversion functions
are enumerated (*[over.match.conv]*), and the best one is chosen through
overload resolution.
Through look at the rule in [over.match.conv], the candidate functions are
formed by:
> The conversion functions of S and its base classes are considered. Those
non-explicit conversion functions that are not hidden within S and *yield
type T or a type that can be converted to type T via a standard conversion
sequence* are candidate functions.
Hence, `operator int() const &` and `operator char() &&` are all candidate
functions, in order to determine which is the best, hence the overload
resolution applies here to find out the best, in the first candidate
function, the implicit parameter object type is `S const&`. Rather, the
implicit parameter object type of the second candidate is `S&&`. In this
case, the argument is of type `S` and a prvalue. According to:
[over.ics.rank#3.2.3]
>S1 and S2 are reference bindings ([dcl.init.ref]) and neither refers to an
implicit object parameter of a non-static member function declared without
a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds
an lvalue reference.
`operator char() &&` wins the game.
And according to:
>The effect of any implicit conversion is the same as performing the
corresponding declaration and initialization and then using the temporary
variable as the result of the conversion. The result is an lvalue if T is
an lvalue reference type or an rvalue reference to function type
([dcl.ref]), an xvalue if T is an rvalue reference to object type, and a
prvalue otherwise. The expression e is used as a glvalue if and only if the
initialization uses it as a glvalue.
That means `char tmp = S{};` where the `tmp` is used as the argument in
function call. A similar process undergoes for `void foo(char)`. Now, the
first example can be simplified to when the argument is of type `char`,
which function is the best when calling foo(tmp). Certainly, it is `void
foo(char)`.
Now, let us consider the second example.
Arguably, it is different from the first example. the parameter of `void
foo(const int &)` is of type `int const&`. So [dcl.init.ref] applies here.
According to [dcl.init.ref#5.1.2]
<https://timsong-cpp.github.io/cppwp/n4659/dcl.init.ref#5.1.2> and
[over.match.ref#1.1]
<https://timsong-cpp.github.io/cppwp/n4659/over.match.ref#1.1>, all
functions that have return type of lvalue reference to type T are candidate
functions, here only `operator const int &() const &` is. And from the type
of this conversion to `const int &` has an identity conversion. Instead,
for the second `void foo(int &&)`, the candidate conversion function is
only `operator int &&() &&` per [dcl.init.ref#5.2.1.2]
<https://timsong-cpp.github.io/cppwp/n4659/dcl.init.ref#5.2.1.2> and
[over.match.ref#1.1].
>From the result of `operator int &&() &&` to `int &&` is also an identity
conversion. Hence, your second example gives an ambiguous error.
Hani Deek via Std-Discussion <std-discussion_at_[hidden]>
于2020年12月30日周三 上午5:05写道:
> Hello,
>
>
> With MSVC, the following code compiles. The char overload is selected by
> overload resolution.
>
> struct S
> {
> operator int() const & { return 0; }
> operator char() && { return 0; }
> };
>
> void foo(int) {}
> void foo(char) {}
>
> int main()
> {
> foo(S{}); //OK, calls 'void foo(char)'.
> }
>
> However, the following code won't compile.
>
> struct S
> {
> int i = 0;
> operator const int &() const & { return i; }
> operator int &&() && { return (int &&)(i); }
> };
>
> void foo(const int &) {}
> void foo(int &&) {}
>
> int main()
> {
> foo(S{}); //error C2668: 'foo': ambiguous call to overloaded function
> //message : could be 'void foo(int &&)'
> //message : or 'void foo(const int &)'
> }
>
> What is exactly the difference that makes the second sample fail to
> compile?
>
> Given that 'operator int &&() &&' is an exact match to the conversion
> required to call 'void foo(int &&)', I expected the compiler to select
> it. It is strange if the C++ rules will not allow the compiler to select a
> conversion function that exactly matches the required conversion, both in
> terms of the provided argument 'S &&' and the result of conversion 'int &&
> '.
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
`int` and the corresponding argument has type `S`, Hence, the following
rules apply here:
> Otherwise, if the source type is a (possibly cv-qualified) class type,
conversion functions are considered. The applicable conversion functions
are enumerated (*[over.match.conv]*), and the best one is chosen through
overload resolution.
Through look at the rule in [over.match.conv], the candidate functions are
formed by:
> The conversion functions of S and its base classes are considered. Those
non-explicit conversion functions that are not hidden within S and *yield
type T or a type that can be converted to type T via a standard conversion
sequence* are candidate functions.
Hence, `operator int() const &` and `operator char() &&` are all candidate
functions, in order to determine which is the best, hence the overload
resolution applies here to find out the best, in the first candidate
function, the implicit parameter object type is `S const&`. Rather, the
implicit parameter object type of the second candidate is `S&&`. In this
case, the argument is of type `S` and a prvalue. According to:
[over.ics.rank#3.2.3]
>S1 and S2 are reference bindings ([dcl.init.ref]) and neither refers to an
implicit object parameter of a non-static member function declared without
a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds
an lvalue reference.
`operator char() &&` wins the game.
And according to:
>The effect of any implicit conversion is the same as performing the
corresponding declaration and initialization and then using the temporary
variable as the result of the conversion. The result is an lvalue if T is
an lvalue reference type or an rvalue reference to function type
([dcl.ref]), an xvalue if T is an rvalue reference to object type, and a
prvalue otherwise. The expression e is used as a glvalue if and only if the
initialization uses it as a glvalue.
That means `char tmp = S{};` where the `tmp` is used as the argument in
function call. A similar process undergoes for `void foo(char)`. Now, the
first example can be simplified to when the argument is of type `char`,
which function is the best when calling foo(tmp). Certainly, it is `void
foo(char)`.
Now, let us consider the second example.
Arguably, it is different from the first example. the parameter of `void
foo(const int &)` is of type `int const&`. So [dcl.init.ref] applies here.
According to [dcl.init.ref#5.1.2]
<https://timsong-cpp.github.io/cppwp/n4659/dcl.init.ref#5.1.2> and
[over.match.ref#1.1]
<https://timsong-cpp.github.io/cppwp/n4659/over.match.ref#1.1>, all
functions that have return type of lvalue reference to type T are candidate
functions, here only `operator const int &() const &` is. And from the type
of this conversion to `const int &` has an identity conversion. Instead,
for the second `void foo(int &&)`, the candidate conversion function is
only `operator int &&() &&` per [dcl.init.ref#5.2.1.2]
<https://timsong-cpp.github.io/cppwp/n4659/dcl.init.ref#5.2.1.2> and
[over.match.ref#1.1].
>From the result of `operator int &&() &&` to `int &&` is also an identity
conversion. Hence, your second example gives an ambiguous error.
Hani Deek via Std-Discussion <std-discussion_at_[hidden]>
于2020年12月30日周三 上午5:05写道:
> Hello,
>
>
> With MSVC, the following code compiles. The char overload is selected by
> overload resolution.
>
> struct S
> {
> operator int() const & { return 0; }
> operator char() && { return 0; }
> };
>
> void foo(int) {}
> void foo(char) {}
>
> int main()
> {
> foo(S{}); //OK, calls 'void foo(char)'.
> }
>
> However, the following code won't compile.
>
> struct S
> {
> int i = 0;
> operator const int &() const & { return i; }
> operator int &&() && { return (int &&)(i); }
> };
>
> void foo(const int &) {}
> void foo(int &&) {}
>
> int main()
> {
> foo(S{}); //error C2668: 'foo': ambiguous call to overloaded function
> //message : could be 'void foo(int &&)'
> //message : or 'void foo(const int &)'
> }
>
> What is exactly the difference that makes the second sample fail to
> compile?
>
> Given that 'operator int &&() &&' is an exact match to the conversion
> required to call 'void foo(int &&)', I expected the compiler to select
> it. It is strange if the C++ rules will not allow the compiler to select a
> conversion function that exactly matches the required conversion, both in
> terms of the provided argument 'S &&' and the result of conversion 'int &&
> '.
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
Received on 2020-12-29 23:48:32