Considering the following example, the line int a = objT + 5; gives ambiguous conversion which is handled in two ways, using the explicit cast which i think shouldn't be necessary and replacing the use of conversion operators with member functions instead. and here, my question becomes should you still use conversions operators or not at all?
class Testable {
public:
Testable(int val = 0):x(val){}
operator int() const { return x; }
operator double() const { return x; }
int toInt() { return x; }
double toDouble() { return x; }
private:
int x;
};
int main()
{
Testable objT(10);
// ambiguous conversion
int a = objT + 5;
// why to use explicit cast when
// the conversion operator is for to handle
// this automatically
int b = static_cast<int>(objT) + 5;
// member functions
int c = objT.toInt() + 5;
}
Note that int d = objT; is unambiguous, as is double e = objT; Here the compiler can unambiguously choose a conversion operator because of the exact match between the type of the left hand side and the return types from those conversion operators.
The ambiguous conversion results from objT + 5 (also note: long f = objT; is also ambiguous). The problem is that you've taken away the essential information the compiler needs to mindlessly perform the disambiguation.
The problem goes away if you get rid of either of those conversion operators. Since the underlying data member is an int, I suggest getting rid of operator double().
For your particular case, you could just provide the conversion to double, since it has a standard conversion to int. However, implicit conversions often do more harm than help. If you use them, then avoid having both the conversion from int (your non explicit constructor) and to int, which I think is the source of your current problem.
This question is similar to mine, whether is it possible to change the precedence of implicit conversion operators. Unfortunately there is no satisfactory solution, there is no precedence amongst implicit conversions.
One solution is to have additional overload(s) to the ambiguous function or operator, in your case they are operator + (const Testable&, int) and operator + (const Testable&, double).
Another solution is to simply leave only one implicit conversion, in your case the integer one would be best.
Only use implicit conversion when you desperately need automatic conversion to the given type. Explicit toInt, toDouble functions are MUCH better, they clarify the code and do not hide potential pitfalls. Use the explicit keyword for unary constructors, they block implicit conversion via that constructor.
And no, it is not possible to chain implicit conversions, neither if they are operators nor constructors. The C++ standard mandates only 1 implicit conversion can be in a conversion sequence.
You don't need implicit conversion to double. You always return decimal value and compiler can combine multiple implicit conversion operators.
If your private property x of type int were a double, I would recommend not to use implicit conversion to int since it would cause precision loss.
Related
template<typename Integral>
struct IntegralWrapper {
Integral _value;
IntegralWrapper() = default;
IntegralWrapper(Integral value)
: _value(value) {}
operator Integral() const {
return _value;
}
operator bool() const = delete;
};
int main() {
IntegralWrapper<int> i1, i2;
i1 * i2;
}
It's compiled successfully by gcc, but failed by MSVC and clang, with error overloaded operator '*' is ambiguous. The problem comes from the explicit deleted operator bool.
https://godbolt.org/z/nh6M11d98
Which side (gcc or clang/MSVC) is right? And why?
First of all: Deleting a function does not prevent it from being considered in overload resolution (with some minor exceptions not relevant here). The only effect of = delete is that the program will be ill-formed if the conversion function is chosen by overload resolution.
For the overload resolution:
There are candidate built-in overloads for the * operator for all pairs of promoted arithmetic types.
So, instead of using * we could also consider
auto mul(int a, int b) { return a*b; } // (1)
auto mul(long a, long b) { return a*b; } // (2)
// further overloads, also with non-matching parameter types
mul(i1, i2);
Notably there are no overloads including bool, since bool is promoted to int.
For (1) the chosen conversion function for both arguments is operator int() const instantiated from operator Integral() const since conversion from int to int is better than bool to int. (Or at least that seems to be the intent, see e.g. https://github.com/cplusplus/draft/issues/2288 and In overload resolution, does selection of a function that uses the ambiguous conversion sequence necessarily result in the call being ill-formed?).
For (2) however, neither conversion from int or bool to long is better than the other. As a result the implicit conversion sequences will for the purpose of overload resolution be the ambiguous conversion sequence. This conversion sequence is considered distinct from all other user-defined conversion sequences.
When then comparing which of the overloads is the better one, neither can be considered better than the other, because both use user-defined conversion sequences for both parameters, but the used conversion sequences are not comparable.
As a result overload resolution should fail. If I completed the list of built-in operator overloads I started above, nothing would change. The same logic applies to all of them.
So MSVC and Clang are correct to reject and GCC is wrong to accept. Interestingly with the explicit example of functions I gave above GCC does reject as expected.
To disallow implicit conversions to bool you could use a constrained conversion function template, which will not allow for another standard conversion sequence after the user-defined conversion:
template<std::same_as<int> T>
operator T() const { return _value; }
This will allow only conversions to int. If you can't use C++20, you will need to replace the concept with SFINAE via std::enable_if.
Is it possible to forbid implicit conversion between basic types in C++? In particular, I'd like to forbid implicit conversion from unsigned to float or double because of bugs like these:
int i = -5;
...
unsigned u = i; // The dawn of the trouble.
...
double d = u; // The epicenter of the bug that took a day to fix.
I tried something like this:
explicit operator double( unsigned );
Unfortunately, that didn't work:
explicit.cpp:1: error: only declarations of constructors can be ‘explicit’
explicit.cpp:1: error: ‘operator double(unsigned int)’ must be a nonstatic member function
You can't simply remove an implicit standard conversion from the language altogether.
Having said that, there are ways to prevent undesired conversions in some situations. During initialization, you can prevent narrowing conversions by using brace syntax. A conversion between floating and integral types is always considered narrowing (edit: except when the source is an integer constant expression).
int i {-5}; // ok; -5 fits in an int
unsigned u = i; // ok; no check for narrowing using old syntax
double d {u}; // error: narrowing
If you are writing a function that takes a double, you can prevent passing an integral type by adding overloads for integral types, then deleting them.
Here is the code:
class A{
public:
int val;
char cval;
A():val(10),cval('a'){ }
operator char() const{ return cval; }
operator int() const{ return val; }
};
int main()
{
A a;
cout << a;
}
I am running the code in VS 2013, the output value is 10, if I comment out operator int() const{ return val; }, the output value will then become a.
My question is how does the compiler determine which implicit type conversion to choose, I mean since both int and char are possible options for the << operator?
Yes, this is ambiguous, but the cause of the ambiguity is actually rather surprising. It is not that the compiler cannot distinguish between ostream::operator<<(int) and operator<<(ostream &, char); the latter is actually a template while the former is not, so if the matches are equally good the first one will be selected, and there is no ambiguity between those two. Rather, the ambiguity comes from ostream's other member operator<< overloads.
A minimized repro is
struct A{
operator char() const{ return 'a'; }
operator int() const{ return 10; }
};
struct B {
void operator<< (int) { }
void operator<< (long) { }
};
int main()
{
A a;
B b;
b << a;
}
The problem is that the conversion of a to long can be via either a.operator char() or a.operator int(), both followed by a standard conversion sequence consisting of an integral conversion. The standard says that (§13.3.3.1 [over.best.ics]/p10, footnote omitted):
If several different sequences of conversions exist that each convert
the argument to the parameter type, the implicit conversion sequence
associated with the parameter is defined to be the unique conversion
sequence designated the ambiguous conversion sequence. For the
purpose of ranking implicit conversion sequences as described in
13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other
user-defined conversion sequence. *
Since the conversion of a to int also involves a user-defined conversion sequence, it is indistinguishable from the ambiguous conversion sequence from a to long, and in this context no other rule in §13.3.3 [over.match.best] applies to distinguish the two overloads either. Hence, the call is ambiguous, and the program is ill-formed.
* The next sentence in the standard says "If a function that uses the ambiguous conversion sequence is selected as the best viable function, the call will be ill-formed because the conversion of one of the arguments in the call is ambiguous.", which doesn't seem necessarily correct, but detailed discussion of this issue is probably better in a separate question.
It shouldn't compile, since the conversion is ambiguous; and it doesn't with my compiler: live demo. I've no idea why your compiler accepts it, or how it chooses which conversion to use, but it's wrong.
You can resolve the ambiguity with an explicit cast:
cout << static_cast<char>(a); // uses operator char()
cout << static_cast<int>(a); // uses operator int()
Personally, I'd probably use named conversion functions, rather than operators, if I wanted it to be convertible to more than one type.
A debugging session gave the result. One is globally defined operator<< and other one is class method. You guess which one is calling which.
Test.exe!std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > & _Ostr, char _Ch)
msvcp120d.dll!std::basic_ostream<char,std::char_traits<char> >::operator<<(int _Val) Line 292 C++
I am not a language lawyer, but I believe compiler is giving precedence to member-function first.
Operators overloaded within class declaration:
class Asdf{
operator float() const;
Asdf operator+(const Asdf&) const;
Asdf operator+(float);
}
int main()
{
Asdf object1, object2, object3;
//Receiving error: "more than one operator '+' matches these operands"
object1= object2 + object3;
_getch();
return 0;
}
Errors:
:error C2666: 'Asdf::operator +' : 3 overloads have similar conversions
:could be 'Asdf Asdf::operator +(float)'
:'Asdf Asdf::operator +(const Asdf &) const'
When I remove all conversion used with the overloaded float conversion operator the code compiles properly.
Implicit conversion operators tend to invite these kinds of ambiguities, especially when combined with implicit constructors.
From C++ Coding Standards:
Consider overloading to avoid implicit type conversions.
Do not multiply objects beyond necessity (Occam's Razor): Implicit
type conversions provide syntactic convenience (but see Item 40). But
when the work of creating temporary objects is unnecessary and
optimization is appropriate (see Item 8), you can provide overloaded
functions with signatures that match common argument types exactly and
won't cause conversions.
Not all change is progress: Implicit conversions can often do more
damage than good. Think twice before providing implicit conversions to
and from the types you define, and prefer to rely on explicit
conversions (explicit constructors and named conversion functions).
A lot of this goes into efficiency and unexpected behaviors that can result from providing implicit conversions, but ambiguity errors from function overloading are included in the sort of side effects you can easily encounter when providing implicit conversion operators and/or constructors.
Solution: make your operator explicit or try to avoid providing it at all. It may be convenient, but it can invite nasty surprises like this (the compiler error is actually the least nasty).
This is happening because the conversion operator provides an implicit means from your class to a float. Thus, the other addition operators involving floats get in the way.
To remedy this, the best solution if you really want that conversion to be possible is to mark it explicit:
explicit operator float() const;
Asdf a;
float f = static_cast<float>(a);
However, this only works in C++11. In C++03, a better option would be to use a function instead:
float toFloat() const;
Asdf a;
float f = a.toFloat();
Is it possible to override (C-style) casts in C++?
Suppose I have the code
double x = 42;
int k = (int)x;
Can I make the cast in the second line execute some code I wrote? Something like
// I don't know C++
// I have no idea if this has more syntax errors than words
operator (int)(double) {
std::cout << "casting from double to int" << std::endl;
}
The reason I ask is because of the question "Is there any way to get gcc or clang to warn on explicit casts?" and my suggestion there.
§ 12.3.1/1 "Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions (Clause 4), for initialization (8.5), and for explicit type conversions (5.4, 5.2.9)."
Yes, we can make conversions, but only if one or both sides is a user-defined type, so we can't make one for double to int.
struct demostruct {
demostruct(int x) :data(x) {} //used for conversions from int to demostruct
operator int() {return data;} //used for conversions from demostruct to int
int data;
};
int main(int argc, char** argv) {
demostruct ds = argc; //conversion from int to demostruct
return ds; //conversion from demostruct to int
}
As Robᵩ pointed out, you can add the explicit keyword to either of those conversion functions, which requires the user to explicitly cast them with a (demostruct)argc or (int)ds like in your code, instead of having them implicitly convert. If you convert to and from the same type, it's usually best to have one or both as explicit, otherwise you might get compilation errors.
Yes, but only for your own types. Look at this:
#include <iostream>
struct D {
// "explicit" keyword requires C++11
explicit operator int() { std::cout << __FUNCTION__ << "\n"; }
};
int main () {
int i;
D d;
//i = d;
i = (int)d;
}
So, you cannot create double::operator int(), but you could create MyDouble::operator int().
You can’t overload operators for built-in types, but you can write a conversion operator for a user-defined type:
struct Double {
double value;
operator int() const {
shenanigans();
return value;
}
};
Since your question arose from a need to find explicit casts in code, also be aware that C++ has explicit casting operators. These are not only clearer than C-style casts, but also eminently searchable:
static_cast<T>(x) // Cast based on static type conversion.
dynamic_cast<T>(x) // Cast based on runtime type information.
const_cast<T>(x) // Add or remove const or volatile qualification.
reinterpret_cast<T>(x) // Cast between unrelated pointer and integral types.
Conversions to other types are overloadable operators in C++ (some examples here), but this fact will not help you.
Stroustrup wanted the language to be extensible, but not mutable. Therefore, overloading an operator only extends the operations to new types, but you cannot redefine what happens with any old types.
"However, to avoid absurdities, it is (still) not allowed to provide new meanings for the built-in operators for built-in types. Thus, the language remains extensible but not mutable."