Reputation: 5156
I cannot compile the following code.
void print_number(long n) {
std::cout << n << std::endl;
}
void print_number(float n) {
std::cout << n << std::endl;
}
void print_pair(std::pair<std::string, long> p) {
std::cout << std::get<1>(p) << std::endl;
}
void print_pair(std::pair<std::string, float> p) {
std::cout << std::get<1>(p) << std::endl;
}
int main() {
print_number(12l);
print_number(3.4f);
print_pair({"long", 12l});
print_pair({"float", 3.4f});
return 0;
}
print_number
functions work well. However, compiler complains about the overloaded print_pair
functions:
error: call of overloaded ‘print_pair(<brace-enclosed initializer list>)’ is ambiguous
.
Are suffices in <brace-enclosed initializer list>
or std::pair
not working? How can I overload functions receiving a std::pair
parameter?
Upvotes: 5
Views: 1060
Reputation: 5156
Thanks for all answers. I up-voted all of them, however, what I wanted to do was call the function print_pair
with heterogeneous <brace-enclosed initializer list>
s.
What I finally did is:
struct number {
number(long n) {
std::cout << n << std::endl;
}
number(float n) {
std::cout << n << std::endl;
}
};
void print_pair(std::pair<std::string, number> p) {}
In this way, the following function calls can be compiled without the error: call of overloaded ‘print_pair(<brace-enclosed initializer list>)’ is ambiguous
.
print_pair({"long", 12l});
print_pair({"float", 3.4f});
Note that, using two struct
s whose constructors take long
and float
respectively, we will go back to the same problem of ambiguity.
Upvotes: 0
Reputation: 302758
C++'s overload resolution logic doesn't consider piece-wise conversions, it only considers the whole conversion for a given parameter/argument pair.
With this call:
print_pair({"long", 12l});
We have two viable candidates. One candidate takes a std::pair<std::string, long>
and the other takes a std::pair<std::string, float>
.
What C++ doesn't do (and what you might be thinking it does) is consider these parts separately - and say that well, "long"
to std::string
is the same in both cases but 12l
to long
is better than 12l
to float
, as a result the conversion to pair<string, long>
wins. This would be how it would work if instead of having two functions that take pair
s, we had two functions that took two arguments:
print_two("long", 12l); // calls print_two(string, long)
But C++ doesn't work like that. What it does do is consider these parts together. That braced-init-list can be used to construct both std::pair
specializations. Both are viable. Both are user-defined conversions. Neither is better than the other, hence ambiguous. We really have no mechanism in the language to make this kind of piecewise conversion work.
You have to either just say what you mean:
print_pair(std::pair<std::string, long>("long", 12l));
Or take two arguments.
Upvotes: 2
Reputation: 217135
With print_pair({"long", 12l});
, there are 2 possible overloads:
void print_pair(std::pair<std::string, long>)
void print_pair(std::pair<std::string, float>)
{"long", 12l}
has no types, but is valid initialization for both std::pair<std::string, long>
and std::pair<std::string, long>
.
So the call is ambiguous.
print_pair(std::pair{"long", 12l});
would also be ambiguous
as std::pair<const char*, long>
can equally be converted to std::pair<std::string, long>
and std::pair<std::string, long>
.
You have to call it:
print_pair(std::pair{std::string("long"), 12l});
to have exact match.Upvotes: 2
Reputation: 9705
Tl;dr
In order to solve the ambiguity, you can provide the correct type to the compiler:
print_pair(std::make_pair<std::string, long>("long", 12l));
print_pair(std::make_pair<std::string, float>("float", 3.4f));
or
print_pair(std::make_pair(std::string("long"), 12l));
print_pair(std::make_pair(std::string("float"), 3.4f));
The ambiguity problem arises from the fact "long"
and "float"
are not std::string
, but rather const char[]
.
So when you try to construct a std::pair
using the following expression: std::pair{"long", 12l}
, what you obtain is a std::pair<const char[5], long>
.
(The same for the float; i.e., std::pair<const char[5], float>
).
Your overload print_pair
accepts std::pair<std::string, long>
or std::pair<std::string, float>
. None of the previous types match, so the compiler has to perform a conversion. Because of that, it cannot automatically deduct what conversion you want to perform. Both are valid.
For example:
std::pair<const char[5], long>
|
----------------------------------------------------------------
v v
std::pair<std::string, long> std::pair<std::string, float>
To "prove" the problem is the std::string
(neither long
or float
), you can also solve the ambiguity constructing a proper std::string
(instead of an array of char
):
print_pair(std::make_pair(std::string("long"), 12l));
print_pair(std::make_pair(std::string("float"), 3.4f));
Starting from C++17, this boilerplate can be simplified with:
using namespace std::string_literals;
print_pair(std::pair{"long"s, 12l});
print_pair(std::pair{"float"s, 3.4f});
Upvotes: 3