When I compile the code below with g++ 4.8.1 (64bit) in this way:
$ g++ -Wconversion -o main main.cpp
I get this result:
main.cpp: In function ‘int main()’:
main.cpp:12:20: warning: conversion to ‘int’ from ‘long unsigned int’ may alter its value [-Wconversion]
int i = sizeof(x)/sizeof(x[0]);
^
My expectation would be that the compiler should be able to evaluate the expression at compile time. If you make a similar program in plain c, gcc works like a charm.
Should this be considered a bug in g++ (e.g. clang++ does not have this problem)?
if you change the problematic line to something like:
char c = 0x10000000/0x1000000;
then the compiler does not complain. This suggest that some constant evaluation is done before warning generation.
main.cpp:
#include <iostream>
struct foo {
int a;
int b;
};
foo x[50];
int main()
{
int i = sizeof(x)/sizeof(x[0]);
std::cout << i << std::endl;
return 0;
}
int i = sizeof(x)/sizeof(x[0]);
//int <-- std::size_t <-- std::size_t / std::size_t
The type of the expression sizeof(x)/sizeof(x[0]) is std::size_t which on your machine is unsigned long int. So conversion from this type to int is data-loss, if the source is bigger in size than the target.
Though, I agree that in your case, there would not be actual data-loss if the compiler actually computes the value, but I guess it applies -Wconversion before the actual computation.
sizeof() returns you std::size_t not int! So cast it or declare i as std::size_t.
std::size_t i = sizeof(x)/sizeof(x[0]);
Related
#include <iostream>
using namespace std;
int main() {
unsigned int u = 5;
int x = -1;
if(x>u) {
cout<<"Should not happen"<<endl;
} else {
cout<<"Ok"<<endl;
}
}
This code outputs Should not happen. I came across this when comparing the size of a string (size_t is an unsigned int or unsigned long long value) to an int.
It seems that C type casts int to unsigned int but in practice, it seems it would bring in bugs. Honestly, I would have preferred a compile-time error given how incompatible int is to unsigned int. I would like to know why the convention is like this?
You can enable -Wsign-compare -Werror in Clang: Try it online!
It'll produce a compile-time error (because of -Werror that treats warnings as errors):
.code.tio.cpp:7:9: error: comparison of integers of different signs: 'int' and 'unsigned int' [-Werror,-Wsign-compare]
if(x>u) {
~^~
1 error generated.
For some reason, -Wall -Werror in Clang (but not in GCC) doesn't produce any errors. But -Wall -Wextra -Werror does include -Wsign-compare, so you can use that.
gcc5.4 doesn't compile the following code:
// source.cpp
int nonconstexprfunc()
{
return 14;
}
constexpr int func(int n)
{
if (n < 0)
return nonconstexprfunc();
return n*n;
}
int main()
{
constexpr int t1 = func(0);
return 0;
}
The command I use:
$ g++ -std=c++14 -c source.cpp
The output:
In function ‘constexpr int func(int)’:
error: ‘constexpr int func(int)’ called in a constant expression
constexpr int t1 = func(0);
In function ‘int main()’:
error: ‘constexpr int func(int)’ called in a constant expression
constexpr int t1 = func(0);
But I can compile that source.cpp using gcc6.4. Doesn't gcc5.4 fully support constexpr functions?
More interestingly I can compile that source.cpp using icpc (Intel C++ compiler) that uses gcc5.4 - I suppose there must be an option to compile that code using gcc5.4.
$ icpc -v
icpc version 19.0 (gcc version 5.4.0 compatibility)
$ icpc -std=c++14 -c source.cpp
no errors
The first limitation is concerning the use gcc 5.4 with -std=c++11 which produces the error because of the two return statement see The body of constexpr function not a return-statement so in order to lift your first issue you need to use -std=c++14
It then produces
'#1 with x86-64 gcc 5.4
: In function 'constexpr int func(int)':
:10:32: error: call to non-constexpr function 'int
nonconstexprfunc()'
return nonconstexprfunc(); ^
: In function 'int main()':
:16:28: error: 'constexpr int func(int)' called in a constant
expression
constexpr int t1 = func(0);
Compiler returned: 1
This next error produced seems to be a known GCC bug (misinterpretation of c++14) see
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86678
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67026
You can also check out calling non constexpr function from constexpr allowed in some conditions
However judging from the error it produces:
It seems pretty obvious that doing
constexpr int nonconstexprfunc()
{
return 14;
}
will solve the error and will be more efficient in your case.
Check the difference with https://www.godbolt.org/ of adding constexpr or not using gcc 8.2 for example.
I want to set the array length to be the minimum of a constant and a generic like this:
template <int foo> struct Bar{
void my_func( int const (&my_array)[std::min(5, foo)] ) { /*...*/ }
};
This code compiles with clang++ but not g++ and I need my code to work with both. The error g++ gives is: error: array bound is not an integer constant before ']' token. How I can set the length of this array to be the minimum of foo and 5?
When I use clang++ I run into the problem that I can't get anything to bind to my_array. I want to run something like:
int main() {
static const int var[5] = {0,1,2,3,4};
Bar<5> bar;
bar.my_func(var);
}
But when I try to compile this code in clang++ I get: error: reference to type 'const int [*]' could not bind to an lvalue of type 'const int [5]'.
If I get rid of the std::min() stuff and replace it with foo the code compiles and runs fine.
Notes:
To get this code to compile you'll need to #include <algorithm> or similar to access std::min.
I don't think that this being part of a template should matter but when I try similar things with non-template function such as:
const int const_five = 5;
void new_func( int const (&my_array)[std::min(5,const_five)] ) { /*...*/ }
g++ says: error: variable or field 'new_func' declared void and clang++ says candidate function not viable: no known conversion from 'const int [5]' to 'const int [std::min(5, const_five)]' for 1st argument which both look like similar problems.
For int const (&my_array)[std::min(5, foo)] to compile, you need a version of std::min which is constexpr. It is since C++14.
Check the default value for -std of gcc and clang you use (its version-dependant). Ultimately, compile with -std=c++14.
Provided by StoryTeller, a nice working MCVE.
Keep it simple:
[foo < 5 ? foo : 5]
Both clang 2.9 and g++ 4.1.2 will generate a warning when the variable x is declared constant in the code snippet below. However when const is removed, as it has been in the snippet, neither of the compilers generates a warning even when executed with the following parameters which are the strictest I know: "-Wall -Wextra -pedantic -ansi"
Why won't the compilers deduce and report the same warning since x isn't volatile and cannot possibly be modified before the type conversion?
#include <iostream>
int main(int argc, char **argv)
{
unsigned int x = 1000;
const unsigned char c = x;
const unsigned int x_ = c;
std::cout << "x=" << x << " x_=" << x_ << std::endl;
return 0;
}
With const unsigned int x = 1000; g++ provides the message "warning: large integer implicitly truncated to unsigned type" and clang "warning: implicit conversion from 'const unsigned int' to 'const unsigned char' changes value from 1000 to 232 [-Wconstant-conversion]".
Is there any way to automatically detect this case without manually inspecting the code or relying on correctly designed unit tests?
For GCC, add the flag -Wconversion and you will get the desired warning. It's not a part of -Wall since so much code just ignores these types of things. I always have it turned on since it finds otherwise hard to debug defects.
If it is a const the compiler can see its value and warn about the truncation. If it is not a const, it cannot, despite the initialisation. This:
const unsigned int x = 1000;
const unsigned char c = x;
is equivalent to:
const unsigned char c = 1000;
I've run gcc with -O3 -fdump-tree-vrp, and what I see in the dump is:
std::__ostream_insert<char, std::char_traits<char> > (&cout, &"x="[0], 2);
D.20752_20 = std::basic_ostream<char>::_M_insert<long unsigned int> (&cout, 1000);
std::__ostream_insert<char, std::char_traits<char> > (D.20752_20, &" x_="[0], 4);
D.20715_22 = std::basic_ostream<char>::_M_insert<long unsigned int> (D.20752_20, 232);
i.e. it just inlines the constants 1000 and 232 in the cout statement!
If I run it with -O0, it doesn't dump anything, despite -ftree-vrp and -ftree-ccp switches.
Seems like gcc inlines the constants before it can emit the warnings...
// sizeofarray.cpp
#include <iostream>
template <typename T,int N>
int size(T (&Array)[N])
{
return N;
}
int main()
{
char p[]="Je suis trop bon, et vous?";
char q[size(p)]; // (A)
return 0;
}
I heard that an array size in C++ must be a constant expression. So char q[size(p)] is invalid, am I right? But I got no errors when I tried
g++ -Wall sizeofarray.cpp
Why?
Like Prasoon says, it's not a constant expression. For now, you can get a constant-expression value of the size of an array like this:
template <std::size_t N>
struct type_of_size
{
typedef char type[N];
};
template <typename T, std::size_t Size>
typename type_of_size<Size>::type& sizeof_array_helper(T(&)[Size]);
#define sizeof_array(pArray) sizeof(sizeof_array_helper(pArray))
Explanation here. You basically encode the size of the array into the size of a type, then get the sizeof of that type, giving you:
char q[sizeof_array(p)];
I heard that an array size in C++ must be a constant expression.
Correct
So char q[size(p)] is invalid, am I right?
According to ISO C++, yes!
But I got no errors when I tried
g++ -Wall sizeofarray.cpp
That's because g++ supports VLA (Variable Length Array) as an extension.
In C++0x there is constexpr feature with the help of which you can write
constexpr int size(T (&Array)[N])
{
return N;
}
and then char q[size(p)] would be legal.
EDIT : Also read this article [blog whatever]
I beg to differ with all the answers here. The code show is perfectly fine except for a minor issue (which is definitely not VLA)
template <typename T,int N>
int size(T (&Array)[N])
{
return N;
}
int main()
{
char p[]="Je suis trop bon, et vous?";
char q[sizeof(p)]; // (A), not sizeof and not size as in OP
return 0;
}
I was wondering that the result of the sizeof is always a const value, and hence the code should be fine.
The above code builds fine on VS 2010 and Comeau(strict mode)
$5.3.3/6- "The result is a constant of
type size_t. [Note: size_t is defined
in the standard header (18.1)."
I use g++ 4.4.3 and have the following alias so that I never forget to turn on the warnings:
$ alias g++
alias g++='g++ -ansi -pedantic -Wall -W -Wconversion -Wshadow -Wcast-qual -Wwrite-strings'
If compiled with the above, there would be some warnings. Following steps show how different options show different warnings.
Compilation with no warning option does not show any warning
$ \g++ sizeofarray.cpp
Turning on -Wall
$ \g++ -Wall sizeofarray.cpp
sizeofarray.cpp: In function ‘int main()’:
sizeofarray.cpp:12: warning: unused variable ‘q’
Turning on -Wextra
$ \g++ -Wall -Wextra sizeofarray.cpp
sizeofarray.cpp: In function ‘int main()’:
sizeofarray.cpp:12: warning: unused variable ‘q’
sizeofarray.cpp: At global scope:
sizeofarray.cpp: In instantiation of ‘int size(T (&)[N]) [with T = char, int N = 27]’:
sizeofarray.cpp:12: instantiated from here
sizeofarray.cpp:4: warning: unused parameter ‘Array’
Finally turning on -pedantic to catch the real problem
$ \g++ -Wall -Wextra -pedantic sizeofarray.cpp
sizeofarray.cpp: In function ‘int main()’:
sizeofarray.cpp:12: warning: ISO C++ forbids variable length array ‘q’
sizeofarray.cpp:12: warning: unused variable ‘q’
sizeofarray.cpp: At global scope:
sizeofarray.cpp: In instantiation of ‘int size(T (&)[N]) [with T = char, int N = 27]’:
sizeofarray.cpp:12: instantiated from here
sizeofarray.cpp:4: warning: unused parameter ‘Array’