I am experiencing some weird errors, and while I have found the reason, what i don't get is the inconsistency of when, exactly it is issued. Please refer to the example code I've posted below.
The problem I see is that I get a conversion error, which to the best of my knowledge should at best be a warning rather than an error, end even then, it's debatable whether it should be a warning at all. The issue the compiler sees is that according to the C++ standard, an int might not be able to be exactly represented as a double value. E. g. a compiler that uses 64 bits for ints could not reliably convert all int values into a double in such a way that it could be converted back to the exact same number.
I understand that reason, but given that VS2019 uses 32 bit ints, IMHO it should at best be a warning to indicate that the code may not be portable.
The inconsistenty I mentioned is that in the example code below, if you remnove the constructor foo::foo(...), suddenly all error messages are converted to warning C4838!
Even more weird, if you do initialize your instances by invoking the constructor (using () instead of {}), there is neither warning nor error!
Why is there a warning and an error code for the exact same thing, and why is this potential problem treated so differently depending on how, exactly, you do your initialization?
Moreover, since typically loss of precision issues are only indicated as warnings, why is it issued as an error here, but only in the specific case that both an instance is initialized with an initializer list is used and a corresponding construtor exists?
P.S.: using Visual Studio 2019 v16.8.5 with standard set to ISO C++17
I mention this because I checked this code against gcc and clang on
godbolt[
^] and found that these three do behave quite differently, and even MSVC shows different behaviour depending on which version you are using.
At least this clarifies the source of at least some of the confusion.
What I have tried:
Here is the example code I put to gether to illustrate my points. The error is not issued if the source value of the conversion is either a literal, a const value, or a constexpr function - this corresponds to the C++ standard which states
Quote:
C++11 8.5.4/7 A narrowing conversion is an implicit conversion [...] from an integer type [...] to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.
struct foo {
foo(int aa, double bb) : a(aa), b(bb) {}
int a;
double b;
};
int foobar() { return 42; }
constexpr int barfoo() { return -42; }
void bar() {
int x = 2 * 3;
foo c{ 1, x }; foo c2( 1, x ); foo d{ 2, 2 };
const int y = 2;
foo e{ 3, y };
foo f{ 4, foobar() }; foo f2( 4, foobar() ); int z = foobar();
foo g{ 5, z }; foo g2( 5, z ); foo h{ 6, barfoo() };
x = barfoo();
foo i{ 6, x }; foo i2( 6, x ); const int cx = barfoo();
foo j{ 7, cx };
}