The following code compiles with MSVC (/std:c++latest) and GCC (-std=c++2c), but not Clang (-std=c++2c). Compiler Explorer demo here.
#include <type_traits>
struct A
{
A(int set_value) : _value{set_value}
{
}
template <typename other>
requires std::is_arithmetic<other>::value
operator other() const
{
return other(_value);
}
A operator*(const A &rhs) const
{
return _value * rhs._value;
}
int _value = 0;
};
int main()
{
(void)(A(3) * -1);
}
Which compiler(s) are handling this code correctly? What is the preferred way to get this working under Clang?
Clang's output from Compiler Explorer:
<source>:26:17: error: use of overloaded operator '*' is ambiguous (with operand types 'A' and 'int')
26 | (void)(A(3) * -1);
| ~~~~ ^ ~~
<source>:16:7: note: candidate function
16 | A operator*(const A &rhs) const
| ^
<source>:26:17: note: built-in candidate operator*(float, int)
26 | (void)(A(3) * -1);
| ^
<source>:26:17: note: built-in candidate operator*(double, int)
<source>:26:17: note: built-in candidate operator*(long double, int)
<source>:26:17: note: built-in candidate operator*(int, int)
[...many similar lines omitted...]
<source>:26:17: note: built-in candidate operator*(unsigned long long, unsigned long)
<source>:26:17: note: built-in candidate operator*(unsigned long long, unsigned long long)
<source>:26:17: note: built-in candidate operator*(unsigned long long, unsigned __int128)
1 error generated.
Compiler returned: 1
(I am trying to create a strong_alias wrapper type which is interchangeable with primitive types, but not other strong_aliases.)
int main()
{
(void)(A(3) * -1);
}
There are several different ways in which this * can be interpreted. It can be A::operator*, which is callable through the implicit conversion of the second operand from int to A, or it can be a built-in arithmetic operator*, which is callable through the implicit conversion of the first operand from A to an arithmetic type (possibly with the second operand also undergoing an implicit conversion to a different arithmetic type). Clang helpfully prints out all the candidates.
The reason why it's ambiguous is that when calling operator* we get an exact match with the first parameter type and a user-defined conversion with the second parameter type. When calling the built-in operator*(int, int), we get a user-defined conversion with the first parameter type and an exact match with the second parameter type. So neither of these candidates is better than the other.
Clang is correct. As for how to get your original code to compile, since the issue is caused by two candidates that are each viable through a different implicit conversion, your options are basically:
operator* that has const strong_alias& as its first parameter type and an arithmetic type as its second parameter type, orA to be explicit, orA::operator other) to be explicit, orI can't tell you which option is correct for you. It depends on the API that you want your type to provide.
The reason why GCC and MSVC don't think it's ambiguous may have to do with the fact that they use a different algorithm for figuring out which built-in operators are candidates. In this particular case they simply give the wrong answer, but more generally the standard does not give enough guidance on how to narrow down a set of relevant built-in candidates in other situations that may potentially involve an unbounded universe of initial candidates. See CWG2844.
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