The following minimal code compiles on g++, but won't compile on clang++:
template<class T>
T operator*(float a, const T& b)
{
return b * a;
}
struct A{
A operator*(float b) const
{
A a;
return a;
}
};
int main()
{
A a;
2.0f * a;
}
This is the error I get:
$ clang++ test.cpp
test.cpp:2:3: error: overloaded 'operator*' must have at least one parameter of
class or enumeration type
T operator*(float a, const T& b)
^
test.cpp:4:11: note: in instantiation of function template specialization
'operator*<float>' requested here
return b * a;
^
test.cpp:18:10: note: in instantiation of function template specialization
'operator*<A>' requested here
2.0f * a;
^
1 error generated.
Clang version 3.5. Is this code valid? Is there a bug on Clang?
2.0f * a;
instantiates ::operator*<A>
. Within that function, we have the expression b * a
, which, if you look at the (simplified) types, is A * float
. At this point, the compiler needs to make a choice. Should that *
be the global function ::operator*<float>
(because the right hand argument is float
), or should it be A::operator*
? To us humans, it's clear it should be A::operator*
, but from the compiler's perspective it's not immediately clear.
So what does the compiler do? It first tries to find all the operator*
functions that could be used (after which, it tries to determine exactly which one to use). One of those operator*
functions that could be used is ::operator*<float>
. But wait, what is ::operator*<float>
? It's float *(float, const float&)
! And we can't do that! You can't overload operators for primitive types (imagine the chaos if you overloaded int +(int, int)
so you make 1 + 2
do something totally different from what everyone expected it to do).
At this point, the program is ill-formed. The mere fact that the compiler even tries to instantiate ::operator*<float>
invalidates the program as a whole. So what can we do? Tell the compiler exactly what to do:
template<class T>
T operator*(float a, const T& b)
{
// This prevents the compiler from instantiating ::operator*<float>
return b.operator*(a);
// The above is meant to illustrate how the fix needs to work: it needs
// to avoid instantiating ::operator*<float>. Other methods can be used
// (like SFINAE) that might be more elegant (check out Walter's answer
// in the duplicate: https://stackoverflow.com/a/18596809/1287251), but
// in the end any solution used must avoid ::operator*<float>.
}
struct A{
A operator*(float b) const
{
A a;
return a;
}
};
int main()
{
A a;
2.0f * a;
}
In short, to answer the question: no, the code is not valid. You must prevent the compiler from trying to instantiate ::operator*<float>
.
This is explained by @dyp in the comments and by @TemplateRex in the duplicate question. However, I had to read their responses several times before I understood what they meant. I've tried to simplify things in this answer. If I can improve it, please let me know!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With