Forbidding implicit `unsigned` to `double` conversion - c++

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.

Related

implicit reinterpret cast on reference without warning/error

Just found the reason for an insidious crash to be a unchecked wild cast by the compiler, disregarding the types. Is this intended behaviour or a compiler bug?
Problem: When a type definition is involved, it is possible to make an implicit reinterpret cast, undermining the type system.
#include <iostream>
template<class A, class B>
inline bool
isSameObject (A const& a, B const& b)
{
return static_cast<const void*> (&a)
== static_cast<const void*> (&b);
}
class Wau
{
int i = -1;
};
class Miau
{
public:
uint u = 1;
};
int
main (int, char**)
{
Wau wau;
using ID = Miau &;
ID wuff = ID(wau); // <<---disaster
std::cout << "Miau=" << wuff.u
<< " ref to same object: " <<std::boolalpha<< isSameObject (wau, wuff)
<< std::endl;
return 0;
}
I was shocked to find out that gcc-4.9, gcc-6.3 and clang-3.8 accept this code without any error and produce the following output:
Miau=4294967295 ref to same object: true
Please note I use the type constructor syntax ID(wau). I would expect such behaviour on a C-style cast, i.e. (ID)wau. Only when using the new-style curly braces syntax ID{wau} we get the expected error...
~$ g++ -std=c++11 -o aua woot.cpp
woot.cpp: In function ‘int main(int, char**)’:
woot.cpp:31:21: error: no matching function for call to ‘Miau::Miau(<brace-enclosed initializer list>)’
ID wuff = ID{wau};
^
woot.cpp:10:7: note: candidate: constexpr Miau::Miau()
class Miau
^~~~
woot.cpp:10:7: note: candidate expects 0 arguments, 1 provided
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(const Miau&)
woot.cpp:10:7: note: no known conversion for argument 1 from ‘Wau’ to ‘const Miau&’
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(Miau&&)
woot.cpp:10:7: note: no known conversion for argument 1 from ‘Wau’ to ‘Miau&&’
Unfortunately, the curly-braces syntax is frequently a no-go in template heavy code, due to the std::initializer_list fiasco. So for me this is a serious concern, since the protection by the type system effectively breaks down here.
Can someone explain the reasoning behind this behaviour?
Is it some kind of backwards compatibility (again, sigh)?
To go full language-lawyer, T(expression) is a conversion of the result of expression to T1. This conversion has as effect to call the class' constructor2. This is why we tend to call a non-explicit constructor taking exactly one argument a conversion constructor.
using ID = Miau &;
ID wuff = ID(wau);
This is then equivalent to a cast expression to ID. Since ID is not a class type, a C-style cast occurs.
Can someone explain the reasoning behind this behaviour?
I really can't tell why is was ever part of C++. This is not needed. And it is harmful.
Is it some kind of backwards compatibility (again, sigh)?
Not necessarily, with C++11 to C++20 we've seen breaking changes. This could be removed some day, but I doubt it would.
1)
[expr.type.conv]
A simple-type-specifier or typename-specifier followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer. [...]
If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression. [...]
2) (when T is of class type and such a constructor exists)
[class.ctor]/2
A constructor is used to initialize objects of its class type. Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the functional notation ([expr.type.conv]) will cause a constructor to be called to initialize an object.
it is possible to make an implicit reinterpret cast, undermining the type system.
ID wuff = ID(wau);
That's not an "implicit" reinterpret cast. That is an explicit type conversion. Although, the fact that the conversion does reinterpretation is indeed not easy to see. Specifically, the syntax of the cast is called "functional style".
If you're unsure what type of cast an explicit type conversion (whether using the functional syntax, or the C style syntax) performs, then you should refrain from using it. Many would argue that explicit type conversions should never be used.
If you had used static_cast instead, you would have stayed within the protection of the type system:
ID wuff = static_cast<ID>(wau);
error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'
It's often also safe to simply rely on implicit conversions:
ID wuff = wau;
error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'
Is this intended behaviour
Yes.
or a compiler bug?
No.

Inhibit implicit conversion while using arithmetic operators

How can we tell the C++ compiler that it should avoid implicit casting while using arithmetic operators such as +and /, i.e.,
size_t st_1, st_2;
int i_1, i_2;
auto st = st_1 + st_2; // should compile
auto i = i_1 + i_2; // should compile
auto error_1 = st_1 + i_2; // should not compile
auto error_2 = i_1 + st_2; // should not compile
// ...
Unfortunately the language specifies what should happen when you add an int to a size_t (see its rules for type promotion) so you can't force a compile time error.
But you could build your own add function to force the arguments to be the same type:
template <class Y>
Y add(const Y& arg1, const Y& arg2)
{
return arg1 + arg2;
}
The constant references prevent any type conversion, and the template forces both arguments to be the same type.
This will always work in your particular case since size_t must be an unsigned type:
The best answer I can give you is to use units: give a look at boost unit.
Another interesting method is the use of opaque typedef you can give a look at this paper Toward Opaque Typedef here a very interesting talk and implementation.
Hope the material can be useful
With built in (non-class) types, it is not possible to prevent unwanted implicit type conversions.
Some compilers can be configured to give warnings for operations involving suspicious conversions, but that does not cover all possible implicit conversions (after all, a conversion from short to long is value preserving, so not all compilers will report it as suspicious). And some of those compiles may also be configured to give errors where they give warnings.
With C++ class types, it is possible to prevent implicit conversions by making constructors explicit, and not defining conversion operators (for example, a class member function named operator int()).
It is also possible for a class type to supply numeric operators (operator+(), etc), which only accept operands of the required types. The problem is that this doesn't necessarily prevent promotion of built in types participating in such expressions. For example, a class that provides an operator+(int) const (so some_object = some_other_object + some_int would work) would not stop an expression like some_other_object + some_short from compiling (as some_short can be implicitly promoted to int).
Which basically means it is possible to prevent implicit conversions to class types, but not prevent promotions occurring in expressions with numeric operators.

How come an user defined conversion is a better match than a standard integer conversion?

You can find the text below in Appendix B of the book "C++ Templates The Complete Guide" by David Vandevoorde and Nicolai Josuttis.
B.2 Simplified Overload Resolution
Given this first principle, we are left with specifying how well a
given argument matches the corresponding parameter of a viable
candidate. As a first approximation we can rank the possible matches
as follows (from best to worst):
Perfect match. The parameter has the type of the expression, or it has a type that is a reference to the type of the expression (possibly
with added const and/or volatile qualifiers).
Match with minor adjustments. This includes, for example, the decay of an array variable to a pointer to its first element, or the
addition of const to match an argument of type int** to a parameter of
type int const* const*.
Match with promotion. Promotion is a kind of implicit conversion that includes the conversion of small integral types (such as bool,
char, short, and sometimes enumerations) to int, unsigned int, long or
unsigned long, and the conversion of float to double.
Match with standard conversions only. This includes any sort of standard conversion (such as int to float) but excludes the implicit
call to a conversion operator or a converting constructor.
Match with user-defined conversions. This allows any kind of implicit conversion.
Match with ellipsis. An ellipsis parameter can match almost any type (but non-POD class types result in undefined behavior).
A few pages later the book shows the following example and text (emphasis mine):
class BadString {
public:
BadString(char const*);
...
// character access through subscripting:
char& operator[] (size_t); // (1)
char const& operator[] (size_t) const;
// implicit conversion to null-terminated byte string:
operator char* (); // (2)
operator char const* ();
...
};
int main()
{
BadString str("correkt");
str[5] = 'c'; // possibly an overload resolution ambiguity!
}
At first, nothing seems ambiguous about the expression str[5]. The
subscript operator at (1) seems like a perfect match. However, it is
not quite perfect because the argument 5 has type int, and the
operator expects an unsigned integer type (size_t and std::size_t
usually have type unsigned int or unsigned long, but never type int).
Still, a simple standard integer conversion makes (1) easily viable.
However, there is another viable candidate: the built-in subscript
operator. Indeed, if we apply the implicit conversion operator to str
(which is the implicit member function argument), we obtain a pointer
type, and now the built-in subscript operator applies. This built-in
operator takes an argument of type ptrdiff_t, which on many platforms
is equivalent to int and therefore is a perfect match for the argument
5. So even though the built-in subscript operator is a poor match (by user-defined conversion) for the implied argument, it is a better
match than the operator defined at (1) for the actual subscript! Hence
the potential ambiguity.
Note that the first list is Simplified Overload Resolution.
To remove the "possibly" and "potential" about whether or not int is the same as ptrdiff_t, let's change one line:
str[(ptrdiff_t)5] = 'c'; // definitely an overload resolution ambiguity!
Now the message I get from g++ is:
warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
and --pedantic-errors promotes that warning to an error.
So without diving into the standard, this tells you that the simplified list is only part of the story. That list tells you which of several possible routes to prefer when going from A to B, but it does not tell you whether to prefer a trip from A to B over a trip from C to D.
You can see the same phenomenon (and the same g++ message) more obviously here:
struct S {
explicit S(int) {}
operator int() { return 0; }
};
void foo(const S&, long) { }
void foo(int, int) { }
int main() {
S s(0);
foo(s, 1);
}
Again the call to foo is ambiguous, because when we have a choice of which argument to implicitly convert in order to choose an overload, the rules don't say, "pick the more lightweight conversion of the two and convert the argument requiring that conversion".
Now you're going to ask me for the standard citation, but I shall use the fact I need to hit the hay as an excuse for not looking it up ;-)
In summary it is not true here that "a user defined conversion is a better match than a standard integer conversion". However in this case the two possible overloads are defined by the standard to be equally good and hence the call is ambiguous.
str.operator[](size_t(5));
Write code in definite way and you'll not need to bother about all this stuff :)
There is much more to thing about.

are casts overridable operations? if so, how?

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."

When to use conversion operators?

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.