Click here to Skip to main content
14,927,434 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
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
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 };// error
    foo c2( 1, x );// no error!
    foo d{ 2, 2 };
	const int y = 2;
	foo e{ 3, y };
    foo f{ 4, foobar() };// error
    foo f2( 4, foobar() );// no error!
	int z = foobar();
    foo g{ 5, z };// error
    foo g2( 5, z );// no error!
	foo h{ 6, barfoo() };
	x = barfoo();
    foo i{ 6, x }; // error
    foo i2( 6, x ); // no error!
	const int cx = barfoo();
	foo j{ 7, cx };
Updated 26-Apr-21 2:53am

1 solution

Some interesting comments on the braces versus parentheses issue at c++ - Why is list initialization (using curly braces) better than the alternatives? - Stack Overflow[^].
Stefan_Lang 26-Apr-21 8:42am
Thanks for the link. The first answer clears up much of my confusion: "List initialization does not allow narrowing".

This at least clarifies why list initialization behaves differently, and it may explain why in this case you get an error rather than just a warning.

It doesn't explain though why this depends on whether or not there is a special constructor in struct foo.

I've checked this code on godbolt, and I get very different behaviour depending on compiler and specific compiler version: some MSVC compiler versions give me 4 errors, some give me 6 errors (they don't accept the instantiations of foo e() and foo j()), whereas gcc at most gives me some warnings.

So maybe part of my confusion comes from MSVC being overly dramatic by turning those warnings into errors...

P.S.: just noticed that clang also issues errors rather than warnings, but unlike MSVC it doesn't change its mind depending on the presencee of the foo() constructor!
Richard MacCutchan 26-Apr-21 8:54am
I get the feeling that as C++ is evolving it is becoming more strict with certain things that we developers have taken for granted in the past.
Stefan_Lang 26-Apr-21 9:05am
I do appreciate that development, but I wish that at least the mainstream compilers would show consistent behaviour! It's not like list initialization turned up yesterday!
Richard MacCutchan 26-Apr-21 9:12am
Yes, but then life would be so boring.
CPallini 26-Apr-21 12:04pm

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900