Reputation: 53
I wondering which overload method resolution rule was applied here.
My purpose was create a new temporary instance using copy constructor, and then pass that object to the method, so r-value reference passing.
And there are overloaded methods that accept l-value, r-value, so I expected r-value overloaded method will be invoked, but It was not.
class Kdy {
public:
Kdy() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Kdy(Kdy&&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Kdy(const Kdy&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void DoAction(const Kdy&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void DoAction(Kdy&&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
}; // Kdy
int main() {
Kdy kdy1;
Kdy kdy2;
// DoAction(const Kdy&), Why??
// kdy1.DoAction(Kdy(kdy2))
kdy1.DoAction({kdy2});
// Then why this works?
// After copy-ctor, DoAction(Kdy&&) was invoked.
kdy1.DoAction({ {kdy2} });
// Then why this dosen't compile?
// Since { {kdy2} } becomes Kdy&&
// { { {kdy2} } } should be Kdy(Kdy&&)
// kdy1.DoAction({ { {kdy2} } });
return 0;
}
I have read a overload reference document multiple times, but it half clear to me https://en.cppreference.com/w/cpp/language/overload_resolution
It seems after collecting a set of candidate methods, compiler make a decision which method best matches by their matching priority.
So obviously if there are some methods accept std::initializer_list<Kdy>
as parameter then those methods are selected. (I have tested it)
Still confusing then
if exact signature matching was failed,
which resolution overload rule was applied in this context?
What made compile think match {kdy2}
best suit for const Kdy&
than for Kdy&&
?
Also, why { { { kdy2 } } }
couldn't be interpreted as Kdy(Kdy&&)
?
Please shed light on this poor guy. Thank you!
Upvotes: 1
Views: 87
Reputation: 11250
Let's take the following part of the standard as reference:
- (3.7) Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution.
- (3.9) Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element ...
Section (3.9) explain why DoAction({kdy2})
select the overload DoAction(const Kdy&)
. The single element of the initializer list is an lvalue of type Kdy
and from the two overloads of DoAction
only one can bind to an lvalue; the one selected.
In DoAction({ {kdy2} })
the initializer don't have a single element of type Kdy
, (3.9) is not used and a prvalue is introduced for {{kdy2}}
. By (3.7) the constructors of Kdy
are considered. Candidates are Kdy(Kdy&&)
and Kdy(Kdy const&)
.
In order to select the best one, {kdy}
is tried to be converted to the parameters of the ctors and again applying (3.9) the selected constructor is the copy ctor. The prvalue is then bound to the parameter of DoAction
and from these overload DoAction(Kdy&&)
is a better match.
For DoAction({ { {kdy2} } })
attemps are made as in the second case but when trying to convert {{kdy2}}
to the parameters of the constructors it fails because the initializer list don't have a single element of type Kdy
and (3.7) don't apply.
Upvotes: 3