Bugs you expected to throw a compiler error ...

Started by
23 comments, last by alvaro 12 years, 11 months ago
I'm sorry, when I tried to compile this thread I got an "error C2065: 'humor' : undeclared identifier". :/
Advertisement
#include <iostream>

struct Price {
double x;

explicit Price(double x) : x(x) {
}

operator double() {
return x;
}
};

Price get_price() {
return Price(5.2);
}

int main() {
bool some_condition = false;
double price = some_condition ? 0 : get_price();
std::cout << price << '\n';
}


With g++-2.95.2 you used to get "5.2". Then after g++-3 you would get "5". It turns out "5" is the correct answer according to the standard, but it would have been nice to get a warning about loss of precision. This actually created a problem at work that took a lot of effort to fix.
It turns out "5" is the correct answer according



Huh, I guess I don't see why. Cool example though!

[quote name='alvaro' timestamp='1306167579' post='4814635']It turns out "5" is the correct answer according



Huh, I guess I don't see why. Cool example though!
[/quote]

Covariance and auto type conversion.

0 is integer.
function returns a double.

Ternary operator can only work on same type: int = cond ? int : int, for example, or more generally, T = cond ? T : T. Ternary operator should throw an error for: X = cond ? T : U. Imagine T = string and U = vector<int>. There is no possible conversion and no type that could hold both.

I know that in Java this is an error. It should be in C/C++ as well, but it may go unreported since with primitive types casting and default construction/copy construction converts the types on the fly. Since Price has a () operator defined, the returned Price is evaluated, it returns double, which is cast to int. Or the int is converted to double.


This example points more to dangers of operator().
The bizarre thing about this example is that get_price() used to return a double, and in that case the answer is 5.2, but then someone changed it to return a type that has an implicit conversion to double, and the behavior changed completely.

The standard says that if the two types to the sides of the `:' are different, the compiler will try to find both conversions of one type into the other. If only one succeeds, that's what will happen.

In the case of, (bool ? int : double), it is somehow determined that the promotion from int to double is preferable. When you change it to (bool ? int : Price), an int cannot be converted to a Price, but a Price can be converted to an int through double.

One last thing that makes this example horrible is that g++ will warn you that you may lose precision if you convert a double to an int, but in this complicated case it doesn't give you any warnings.

[quote name='mozie' timestamp='1306275681' post='4815338']
[quote name='alvaro' timestamp='1306167579' post='4814635']It turns out "5" is the correct answer according



Huh, I guess I don't see why. Cool example though!
[/quote]

Covariance and auto type conversion.

0 is integer.
function returns a double.

Ternary operator can only work on same type: int = cond ? int : int, for example, or more generally, T = cond ? T : T. Ternary operator should throw an error for: X = cond ? T : U. Imagine T = string and U = vector<int>. There is no possible conversion and no type that could hold both.

I know that in Java this is an error. It should be in C/C++ as well, but it may go unreported since with primitive types casting and default construction/copy construction converts the types on the fly. Since Price has a () operator defined, the returned Price is evaluated, it returns double, which is cast to int. Or the int is converted to double.


This example points more to dangers of operator().
[/quote]

Thanks! So, would changing the 0 to a 0.0 resolve it? Because then it would result in double = (cond) ? double : double.

Thanks! So, would changing the 0 to a 0.0 resolve it? Because then it would result in double = (cond) ? double : double.


Yes, changing the 0 to 0.0 solves the problem. I now always make my constants the type that I intend them to be (0 for int, 0u for unsigned, 0.0 for double, 0.0f for float, T(0) in a template).
Yes, changing the 0 to 0.0 solves the problem. I now always make my constants the type that I intend them to be (0 for int, 0u for unsigned, 0.0 for double, 0.0f for float, T(0) in a template).
I had a similar bug recently with bit-shifting:u64 mask;
mask = 1 << 30;
assert( mask == 0x40000000 );
mask = 1 << 31;
assert( mask == 0x80000000 );
mask = 1 << 32;
assert( mask == 0x100000000 );//fail
This was failing because the literal '1' is a 32-bit int, so you can't get a 64-bit result from the shift operation. The solution is to use the correct type of literal '1' as above.
The bug snuck in because [font="Courier New"]mask[/font] was originally a [font="Courier New"]u32[/font] and was later upgraded to a [font="Courier New"]u64[/font] without scrutinising all the code that used the variable...

#include <iostream>

class Example
{
public:
Example(int va1ue)
:
value(value)
{
}

int value;
};

int main()
{
Example e(42);
std::cout << "What? " << e.value << '\n';
}

I had some code similar to above and it drove me demented looking for the problem. Like my example, the type involved was so trivial that I didn't really read it I kept assuming the problem was elsewhere.
It took me a while to see the problem with rip-off's code. I don't know why g++ doesn't complain about use of uninitialized variables unless you compile with -O.

This topic is closed to new replies.

Advertisement