Compiling with g++ -std=c++0x, using gcc-4.5.0 : struct A { char x; }; template<char C> void f() { char y = 42; A a = { y+C }; } int main() { f<1>(); } yields an "error: narrowing conversion of ‘(((int)y) + 8)’ from ‘int’ to ‘char’ inside { }". If I change the template parameter type from "char C" to "int C" the error message persists, this seems wrong too, but I am not quite shre. If I leave out the "y", everything is fine.
I wonder what the C++ standard said because we have the same issue in Wconversion and Joseph rejected my patch arguing that the operation was done in the larger type, so there was a narrowing conversion. I still believe that we should not warn for T = T op T, when T is the same type all the time.
Sorry for the unicode mess. The error message is 'error: narrowing conversion of "(((int)y) + 1)" from "int" to "char" inside { }'. The same error happens with a non templated function, but if I use two template parameters, the error disappears, even if they are to large. So this is at least very inconsistent. no error: struct A { <-->char x; }; template<char C, char D>void f() { <-->A a = { C+D }; } int main() { <-->f<1,2>(); } still no error: struct A { <-->char x; }; template<int C, int D>void f() { <-->A a = { C+D }; } int main() { <-->f<1,2>(); } error: struct A { <-->char x; }; void f(char C, char D) { <-->A a = { C+D }; } int main() { <-->f(1,2); } I believe I should not get an error, even if the template parameter type is larger than a char, as long as the template parameter value fits in in char, so template<int C> void f() { char y = 42; A a = { y+C }; } should give no error, as long as C fits in a char. IMHO ;-)
'y' and 'C' are both promoted to int and 'y' is not a constant, so the resulting value cannot be proven to fit in a char, so it's a narrowing conversion. If you make 'y' a const int then the value y+C is a constant and can be proven not to narrow. The fact that the optimiser knows y=42 is irrelevant, the language specifies that y is not a constant, and whether the code is valid or not has to be independent of optimisations such as value propagation. I think gcc is correct here.
(In reply to comment #2) > Sorry for the unicode mess. The error> I believe I should not get an error, even if the template parameter type is > larger than a char, as long as the template parameter value fits in in char, so > > template<int C> void f() { > char y = 42; > A a = { y+C }; > } > > should give no error, as long as C fits in a char. IMHO ;-) Just to be extra clear: C is a constant and the narrowing is *not* due to C, consider: struct A { char x; }; template<int C> void f() { A a = { C }; } int main() { f<1>(); } Here C is an int, but it's a constant and it is provable that no narrowing takes palce. The problem in your example is 'y' not 'C' because the value of 'y' is not a constant.
So is it provable that for a "T op T" to be stored in T no narrowing takes place? If the answer for T == char is no and for T == int it is yes this is rather fishy ;-)
(In reply to comment #5) > So is it provable that for a "T op T" to be stored in T no narrowing takes > place? Yes, if the values are constants. > If the answer for T == char is no and for T == int it is yes this is rather > fishy ;-) That's not what I said. Look: #include <climits> struct A { char x; }; template<char C, char D> void f() { A a = { C+D }; } template<int I, int J> void g() { A a = { I+J }; } int main() { f<1, 1>(); // OK g<1, 1>(); // OK f<CHAR_MAX, CHAR_MAX>(); // Error } f<1,1>() is ok, because C and D are constants. The type doesn't matter, the result of C+D is known at compile time and fits in a char. g<1,1>() is ok, because I and J are constants. The type doesn't matter, the result of I+J is known at compile time and fits in a char. f<CHAR_MAX, CHAR_MAX>() is not ok, because the result is known at compile time and doesn't fit in a char. See 8.5.4 [dcl.init.list]p6 in teh C++0x draft for the full rules
He is referring to a testcase like: template<typename T, T C> void f() { struct A { T x; }; T y = 42; A a = { y + C }; } int main() { f<int,1>(); f<char,1>(); } So, we warn for T == char but not for T == int. I know that the standard considers differently narrowing and overflow but the difference is still surprising.
(In reply to comment #7) > He is referring to a testcase like: > > template<typename T, T C> void f() { > struct A { > T x; > }; > > T y = 42; > A a = { y + C }; > } > > int main() { > f<int,1>(); > f<char,1>(); > } > > So, we warn for T == char but not for T == int. I know that the standard Note it's not a warning, it's an error, mandated by the standard. > considers differently narrowing and overflow but the difference is still > surprising. In both cases, T+T has type int, so obviously it fits in an int. It doesn't necessarily fit in an char, so is an error unless the values are constants and the actual value can fit in a char. This is mandated by the standard and the diagnostic is IMHO clear.
I understand now after the implicit promotion to int of a non constant value the result of the narrowing operation can't be guaranteed to fit in the original type. But I still think it shouldn't give an error, and if the standard says so, I think it is flawed in this regard ;-) Consider g<INT_MAX, INT_MAX>(); // Warning, but no Error despite it can be proven that the value will not fit and this is very likely an error. Opposing to char c,d; A a = { c+d }; which is very likely not an error and would only require a mild warning. IMHO. Manuel, in your testcase, you do not only warn, you error out if compiled with -std=c++0x.
(In reply to comment #8) > > In both cases, T+T has type int, We know that, but I don't think most C/C++ programmers know about integer promotion rules. We just disagree here. But since this is mandated by the standard, you are right. > so obviously it fits in an int. It doesn't For a strict-standard definition of "fits", because it may overflow and a layman wouldn't say that it "fits" in that case. > This is mandated by the standard and the diagnostic is IMHO clear. I am not arguing against that (although, I think it is unfortunate). I would prefer a bit longer message: error: C++0x does not allow narrowing conversion of "(((int)y) + 1)" from "int" to "char" inside { } (BTW, I think those braces should be within quotes). But since I guess I am in the minority here, we'll have to close this as INVALID.
(In reply to comment #9) > I understand now after the implicit promotion to int of a non constant value > the result of the narrowing operation can't be guaranteed to fit in the > original type. But I still think it shouldn't give an error, and if the > standard says so, I think it is flawed in this regard ;-) > > Consider > > g<INT_MAX, INT_MAX>(); // Warning, but no Error The integer overflow means this is undefined behaviour. But it is not a narrowing conversion according to the rules of 8.5.4/6 > despite it can be proven that the value will not fit and this is very likely an > error. Opposing to > > char c,d; > A a = { c+d }; > > which is very likely not an error and would only require a mild warning. IMHO. use A a = { char(c+d) } if you want the result to be a char not an int, then there is no narrowing conversion, because a narrowing conversion is an impliit conversion. (In reply to comment #10)iagnostic is IMHO clear. > prefer a bit longer message: > > error: C++0x does not allow narrowing conversion of "(((int)y) + 1)" from "int" > to "char" inside { } I prefer the shorter message. If the compiler tells you there is an error it doesn't normally tell you the standard says so. If you compile with -std=c++0x then obviously that's the standard in question.
I am closing this, as it isn't a gcc bug, as it behaves according to the standard. The bug is in the standard, as it mandates f<1,1> // ok f<CHAR_MAX, CHAR_MAX>() // error g<INT_MAX, INT_MAX>() // no error, but undefined behaviuour f(char, char) // error g(int, int) // ok which is inconsistent and surprising. C++0x should really have got rid of the implicit integer promotion. Wasn't the intent of the implicit promotion to be able to write char a,b,c,d; a = b*c/d; and get a correct result even if b*c > CHAR_MAX? I believe nobody does write code like this anymore, and even if, you could simply say "undefined behaviour" ;-) It doesn't work for ints anyway. Instead I have now an implicit integer promotion which forces me to use an explicit cast in compound initializers, where narrowing conversion isn't allowed, while in a simple assignment of course it is allowed (or else a hell would break loose... ). Why not make -Wconversion an error, at least this would be consistent ;-)
...
(In reply to comment #12) > Why not make -Wconversion an error, at least this would > be consistent ;-) You can use -Werror=conversion
I agree that this is a bug, but it's a bug in C++0x, not with GCC, and unfortunately it came up too late for me to include it as a national body comment. I have submitted it as a defect report.
The committee closed my DR as not a defect. We could still downgrade this error to a pedwarn, though. http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_closed.html#1078
Actually, it's already a permerror, so -fpermissive will allow your code to compile without changes.
I have chosen the "recommended" way and added a cast, -fpermissive would allow to many other dubious constructs to pass. Still I think c++ should get rid of implicit integer conversions :-)