Why isn't this call to a template ambiguous?
Clash Royale CLAN TAG#URR8PPP
up vote
12
down vote
favorite
I declare two templates, the first converts the argument x
from type T
to type U
and the second from type U
to type T
. If I call cast
with 10, the compiler does not complain. I think both meet the requirements to be used and therefore there should be ambiguity, is that true? This code prints 10.
#include <iostream>
template<typename T, typename U>
U cast(T x)
return static_cast<U>(x);
template<typename T, typename U>
T cast(U x)
return static_cast<T>(x);
int main()
std::cout << cast<int,float>(10) << 'n';
c++ templates overload-resolution template-deduction
New contributor
add a comment |Â
up vote
12
down vote
favorite
I declare two templates, the first converts the argument x
from type T
to type U
and the second from type U
to type T
. If I call cast
with 10, the compiler does not complain. I think both meet the requirements to be used and therefore there should be ambiguity, is that true? This code prints 10.
#include <iostream>
template<typename T, typename U>
U cast(T x)
return static_cast<U>(x);
template<typename T, typename U>
T cast(U x)
return static_cast<T>(x);
int main()
std::cout << cast<int,float>(10) << 'n';
c++ templates overload-resolution template-deduction
New contributor
If you change your code tocast<int,float>(10.0f)
the second overload would be called. And if you changed it tocast<int,float>(10.0)
the call would be ambiguous.
â Praetorian
5 hours ago
add a comment |Â
up vote
12
down vote
favorite
up vote
12
down vote
favorite
I declare two templates, the first converts the argument x
from type T
to type U
and the second from type U
to type T
. If I call cast
with 10, the compiler does not complain. I think both meet the requirements to be used and therefore there should be ambiguity, is that true? This code prints 10.
#include <iostream>
template<typename T, typename U>
U cast(T x)
return static_cast<U>(x);
template<typename T, typename U>
T cast(U x)
return static_cast<T>(x);
int main()
std::cout << cast<int,float>(10) << 'n';
c++ templates overload-resolution template-deduction
New contributor
I declare two templates, the first converts the argument x
from type T
to type U
and the second from type U
to type T
. If I call cast
with 10, the compiler does not complain. I think both meet the requirements to be used and therefore there should be ambiguity, is that true? This code prints 10.
#include <iostream>
template<typename T, typename U>
U cast(T x)
return static_cast<U>(x);
template<typename T, typename U>
T cast(U x)
return static_cast<T>(x);
int main()
std::cout << cast<int,float>(10) << 'n';
c++ templates overload-resolution template-deduction
c++ templates overload-resolution template-deduction
New contributor
New contributor
edited 12 mins ago
Deduplicator
33.2k64786
33.2k64786
New contributor
asked 6 hours ago
wic
1087
1087
New contributor
New contributor
If you change your code tocast<int,float>(10.0f)
the second overload would be called. And if you changed it tocast<int,float>(10.0)
the call would be ambiguous.
â Praetorian
5 hours ago
add a comment |Â
If you change your code tocast<int,float>(10.0f)
the second overload would be called. And if you changed it tocast<int,float>(10.0)
the call would be ambiguous.
â Praetorian
5 hours ago
If you change your code to
cast<int,float>(10.0f)
the second overload would be called. And if you changed it to cast<int,float>(10.0)
the call would be ambiguous.â Praetorian
5 hours ago
If you change your code to
cast<int,float>(10.0f)
the second overload would be called. And if you changed it to cast<int,float>(10.0)
the call would be ambiguous.â Praetorian
5 hours ago
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
21
down vote
accepted
When you use cast<int, float>
, both templates are considered.
template<typename T=int,typename U=float>
U cast(T x);
template<typename T=int,typename U=float>
T cast(U x);
we then substitute:
template<typename T=int,typename U=float>
float cast(int x);
template<typename T=int,typename U=float>
int cast(float x);
at this point, there are no types to deduce. So we go to overload resolution.
In one case, we can take an int
and convert to float
when calling cast, and in the other we take a int
and convert to int
when calling cast. Note I'm not looking at all into the body of cast; the body is not relevant to overload resolution.
The second non-conversion (at the point of call) is a better match, so that overload is chosen.
If you did this:
std::cout << cast<int>(10) << "n";
things get more interesting:
template<typename T=int,typename U=?>
U cast(T x);
template<typename T=int,typename U=?>
T cast(U x);
for the first one, we cannot deduce U
. For the second one we can.
template<typename T=int,typename U=?>
U cast(int x);
template<typename T=int,typename U=int>
int cast(int x);
so here we have one viable overload, and it is used.
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
1
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
add a comment |Â
up vote
2
down vote
The instantiation is not ambiguous because the function argument exactly matches the first template parameter: the literal 10
is an int
, which is also explicitly specified for the first template type.
You can make the instantiation ambiguous when the argument type does not match the explicitly specified types (thus, a conversion is necessary), e.g.
// error: call of overloaded 'cast<int, float>(unsigned int)' is ambiguous
std::cout << cast<int,float>(10u) << "n";
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
21
down vote
accepted
When you use cast<int, float>
, both templates are considered.
template<typename T=int,typename U=float>
U cast(T x);
template<typename T=int,typename U=float>
T cast(U x);
we then substitute:
template<typename T=int,typename U=float>
float cast(int x);
template<typename T=int,typename U=float>
int cast(float x);
at this point, there are no types to deduce. So we go to overload resolution.
In one case, we can take an int
and convert to float
when calling cast, and in the other we take a int
and convert to int
when calling cast. Note I'm not looking at all into the body of cast; the body is not relevant to overload resolution.
The second non-conversion (at the point of call) is a better match, so that overload is chosen.
If you did this:
std::cout << cast<int>(10) << "n";
things get more interesting:
template<typename T=int,typename U=?>
U cast(T x);
template<typename T=int,typename U=?>
T cast(U x);
for the first one, we cannot deduce U
. For the second one we can.
template<typename T=int,typename U=?>
U cast(int x);
template<typename T=int,typename U=int>
int cast(int x);
so here we have one viable overload, and it is used.
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
1
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
add a comment |Â
up vote
21
down vote
accepted
When you use cast<int, float>
, both templates are considered.
template<typename T=int,typename U=float>
U cast(T x);
template<typename T=int,typename U=float>
T cast(U x);
we then substitute:
template<typename T=int,typename U=float>
float cast(int x);
template<typename T=int,typename U=float>
int cast(float x);
at this point, there are no types to deduce. So we go to overload resolution.
In one case, we can take an int
and convert to float
when calling cast, and in the other we take a int
and convert to int
when calling cast. Note I'm not looking at all into the body of cast; the body is not relevant to overload resolution.
The second non-conversion (at the point of call) is a better match, so that overload is chosen.
If you did this:
std::cout << cast<int>(10) << "n";
things get more interesting:
template<typename T=int,typename U=?>
U cast(T x);
template<typename T=int,typename U=?>
T cast(U x);
for the first one, we cannot deduce U
. For the second one we can.
template<typename T=int,typename U=?>
U cast(int x);
template<typename T=int,typename U=int>
int cast(int x);
so here we have one viable overload, and it is used.
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
1
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
add a comment |Â
up vote
21
down vote
accepted
up vote
21
down vote
accepted
When you use cast<int, float>
, both templates are considered.
template<typename T=int,typename U=float>
U cast(T x);
template<typename T=int,typename U=float>
T cast(U x);
we then substitute:
template<typename T=int,typename U=float>
float cast(int x);
template<typename T=int,typename U=float>
int cast(float x);
at this point, there are no types to deduce. So we go to overload resolution.
In one case, we can take an int
and convert to float
when calling cast, and in the other we take a int
and convert to int
when calling cast. Note I'm not looking at all into the body of cast; the body is not relevant to overload resolution.
The second non-conversion (at the point of call) is a better match, so that overload is chosen.
If you did this:
std::cout << cast<int>(10) << "n";
things get more interesting:
template<typename T=int,typename U=?>
U cast(T x);
template<typename T=int,typename U=?>
T cast(U x);
for the first one, we cannot deduce U
. For the second one we can.
template<typename T=int,typename U=?>
U cast(int x);
template<typename T=int,typename U=int>
int cast(int x);
so here we have one viable overload, and it is used.
When you use cast<int, float>
, both templates are considered.
template<typename T=int,typename U=float>
U cast(T x);
template<typename T=int,typename U=float>
T cast(U x);
we then substitute:
template<typename T=int,typename U=float>
float cast(int x);
template<typename T=int,typename U=float>
int cast(float x);
at this point, there are no types to deduce. So we go to overload resolution.
In one case, we can take an int
and convert to float
when calling cast, and in the other we take a int
and convert to int
when calling cast. Note I'm not looking at all into the body of cast; the body is not relevant to overload resolution.
The second non-conversion (at the point of call) is a better match, so that overload is chosen.
If you did this:
std::cout << cast<int>(10) << "n";
things get more interesting:
template<typename T=int,typename U=?>
U cast(T x);
template<typename T=int,typename U=?>
T cast(U x);
for the first one, we cannot deduce U
. For the second one we can.
template<typename T=int,typename U=?>
U cast(int x);
template<typename T=int,typename U=int>
int cast(int x);
so here we have one viable overload, and it is used.
edited 5 hours ago
answered 5 hours ago
Yakk - Adam Nevraumont
172k18175354
172k18175354
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
1
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
add a comment |Â
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
1
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
what OP's code actually does is convert from int to float (verified in assembler), so what non-conversion?
â jakub_d
5 hours ago
1
1
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
@jakub_d In the overload resolution of the argument. The body of a function does not change which function is called.
â Yakk - Adam Nevraumont
5 hours ago
add a comment |Â
up vote
2
down vote
The instantiation is not ambiguous because the function argument exactly matches the first template parameter: the literal 10
is an int
, which is also explicitly specified for the first template type.
You can make the instantiation ambiguous when the argument type does not match the explicitly specified types (thus, a conversion is necessary), e.g.
// error: call of overloaded 'cast<int, float>(unsigned int)' is ambiguous
std::cout << cast<int,float>(10u) << "n";
add a comment |Â
up vote
2
down vote
The instantiation is not ambiguous because the function argument exactly matches the first template parameter: the literal 10
is an int
, which is also explicitly specified for the first template type.
You can make the instantiation ambiguous when the argument type does not match the explicitly specified types (thus, a conversion is necessary), e.g.
// error: call of overloaded 'cast<int, float>(unsigned int)' is ambiguous
std::cout << cast<int,float>(10u) << "n";
add a comment |Â
up vote
2
down vote
up vote
2
down vote
The instantiation is not ambiguous because the function argument exactly matches the first template parameter: the literal 10
is an int
, which is also explicitly specified for the first template type.
You can make the instantiation ambiguous when the argument type does not match the explicitly specified types (thus, a conversion is necessary), e.g.
// error: call of overloaded 'cast<int, float>(unsigned int)' is ambiguous
std::cout << cast<int,float>(10u) << "n";
The instantiation is not ambiguous because the function argument exactly matches the first template parameter: the literal 10
is an int
, which is also explicitly specified for the first template type.
You can make the instantiation ambiguous when the argument type does not match the explicitly specified types (thus, a conversion is necessary), e.g.
// error: call of overloaded 'cast<int, float>(unsigned int)' is ambiguous
std::cout << cast<int,float>(10u) << "n";
answered 5 hours ago
lubgr
7,64521442
7,64521442
add a comment |Â
add a comment |Â
wic is a new contributor. Be nice, and check out our Code of Conduct.
wic is a new contributor. Be nice, and check out our Code of Conduct.
wic is a new contributor. Be nice, and check out our Code of Conduct.
wic is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52609797%2fwhy-isnt-this-call-to-a-template-ambiguous%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
If you change your code to
cast<int,float>(10.0f)
the second overload would be called. And if you changed it tocast<int,float>(10.0)
the call would be ambiguous.â Praetorian
5 hours ago