Consider the following declaration:
#include <array>
struct X
{
//std::array<bool,3> arr={false,false,false};
bool brr[3]={false,false,false};
};
As is, it compiles normally by g++ 5.2. But if I uncomment the std::array, I get an error:
test.cpp:5:46: error: array must be initialized with a brace-enclosed initializer
std::array<bool,3> arr={false,false,false};
^
test.cpp:5:46: error: too many initializers for ‘std::array<bool, 3u>’
OTOH, this declaration works without problems inside main(). Also, the following initialization does work inside struct X:
std::array<bool,3> arr={{false,false,false}};
Why can't I use the simple initialization with single braces in struct definition?
This looks like a gcc bug see: Bug 65815 - brace elision doesn't work in NSDMI. The report says:
On Page 975 of "The C++ Programming Language", 4th edition, Bjarne
Stroustrup says:
"An array can be initialized by an initializer list: array a1 =
{ 1, 2, 3 };"
and Clang (V 3.5) accepts it. However, G++ 4.9.2 thinks this is an
error:
"error: array must be initialized with a brace-enclosed initializer
const std::array<double, 3> _ar0val = {1.0, -1.0, 1.0};"
The issue was narrowed down to the following test case:
struct array {
int data [2];
};
struct X {
array a = { 1, 2 };
};
It looks like the fix is in the head revision, the OPs code works in that revision, see it live.
As noted in the bug report using an inner set of braces is a possible work-around:
std::array<bool,3> arr={ {false,false,false} };
^ ^
Related
In the following code:
#include <cstring>
template <unsigned len>
struct CharArray {
CharArray() {
memset(data_, 0, len);
}
char data_[len];
};
struct Foobar {
CharArray<5> a;
CharArray<3> b;
CharArray<0> c;
};
int main() {
Foobar f;
}
The type CharArray<0> ends up having a zero-sized array as its only member. I'm aware of this being a GCC extension and unsafe practice in general. The question is not about that.
When I compile the code with gcc 10.2.0, I get the following warning:
<source>: In function 'int main()':
<source>:5:3: warning: array subscript 8 is outside array bounds of 'Foobar [1]' [-Warray-bounds]
5 | CharArray() {
| ^~~~~~~~~
<source>:18:10: note: while referencing 'f'
18 | Foobar f;
| ^
With gcc9 and earlier there's no warning.
Question: Where does the subscript 8 come from? And what is the Foobar [1] mentioned there? It looks like there's an array of one Foobars and we're trying to access element 8 in that array. Not sure how that could happen. If somebody knows the details, I'd appreciate it if you could explain it.
This happens when compiling with gcc++-10 in Ubuntu 20.04 with -O3 -Wall -Wextra as options. If I don't pass any optimization flag, there won't be any warning. Also: if I take the constructor away, the warning will also disappear.
It seems the issue is somehow related to the memset(): as avoiding it using a condition (len != 0) doesn't work it seems the compiler recognizes that the start address of CharArray<0>'s object is produced by the intialization of CharArray<3> and warns about that. This theory can be tested by conditionally not initializing CharArray<3> with memset() or specializing that type as that makes the warning go way:
CharArray() { if (len != 3) memset(data_, 0, len); }
or
template <>
struct CharArray<3> {
CharArray(): data_() { }
char data_[3];
};
The warning is probably spurious. It seems by the time the address of the zero sized array is used the compiler has "forgotten" that it was produced by accessing a different array's member. The easiest approach to avoid the warning seems to correctly initialize the data in the initializer list and not using memset() at all:
template <unsigned len>
struct CharArray {
CharArray(): data_() {}
char data_[len];
};
Doing anything to a zero length C-like array is higly suspicious. Including even defining one in my opinion.
However, you can specialise the constructor to NOT do anything to the zero length array:
template<> CharArray<0>::CharArray() {}
In order to not even define a zero sized array (which I think should not be an obstacle to anythign you might want to achieve with the class in general...), you would have to specialise the whole class. (credits to Dietmar Kühl for this addition)
The CDT parser reports a syntax error for the structure initialization:
typedef struct MyStruct
{
int a;
float b;
};
int main( void )
{
// GNU C extension format
MyStruct s = {a : 1, b : 2};
// C99 standard format
// MyStruct s = {.a = 1, .b = 2};
return 0;
}
While GCC lists the : form as obsolete, it would seem that it has not been deprecated nor removed. In C99 I would certainly use the standard .<name> = form but for C++, the : is the only option that I am aware of for designated initialization.
I have tried setting my toolchain to both MinGW and Cross GCC, but neither seem to work.
How can I get Eclipse to recognize this syntax? It's not a big deal for one line but it carries through to every other instance of the variable since Eclipse does not realize it is declared.
The . form is only available in C99 and not in any flavor of C++. In C++ your only standards-compliant options are ordered initialization or constructors.
You can use chaining with appropriate reference returning methods to create a similar interface (here a and b are methods rather than variables):
MyStruct s;
s.a(1).b(2);
I meet this problems too and i use below method to solve it.
MyStruct s = {
1,
2,
}
This requires programmer to ensure sequence of initialization.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why can't variables defined in a conditional be constructed with arguments?
Consider this simple example:
/*1*/ int main() {
/*2*/ for (int i(7); i;){break;}
/*3*/ if (int i(7)) {}
/*4*/ }
Why line-2 compiles just fine, whilst line-3 gives the error? This is little strange to me why if-statement is in this aspect treated worse than for-loop?
If this is compiler specific - I tested with gcc-4.5.1:
prog.cpp: In function 'int main()':
prog.cpp:3:7: error: expected primary-expression before 'int'
prog.cpp:3:7: error: expected ')' before 'int'
I was inspired by this question
[UPDATE]
I know this compiles just fine:
/*1*/ int main() {
/*2*/ for (int i = 7; i;){break;}
/*3*/ if (int i = 7) {}
/*4*/ }
[UPDATE2]
It seems to be purely academic question - but this could be extremely important for such types as std::unique_ptr<> which cannot be copied:
#include <memory>
int main() {
if (std::unique_ptr<int> i = new (std::nothrow) int(7)) {
}
if (std::unique_ptr<int> i(new (std::nothrow) int(7))) {
}
}
Neither of these two kinds are allowed. Not sure about new C++11 syntax {}?
The C++ standard doesn't provide a rationale but I would suspect that using the constructor notation could cause some inconsistencies. For example, since function declarations aren't allowed in the if statement, the most vexing parse would actually mean what was intended. For example:
int f(); // function declaration (simple form or the most vexing parse)
if (int f()) { // illegal in C++ but, when allowed, would be a zero-initialized int
}
In C++ 2011 you can use brace-initialization:
if (int x{f()}) {
...
}
Unfortunately, brace-initialization doesn't always mean the same thing as using constructor notation (I think it's called direct-initialization).
With respect to the update: you can use one of these:
if (std::unique_ptr<int> p = std::unique_ptr<int>(new int(1))) { ... }
if (auto p = std::unique_ptr<int>(new int(2))) { ... }
if (std::unique_ptr<int>{new int(3)}) { ... }
It seems there are plenty of options :-)
In terms of syntax, the reason is simple.
The first element of the for statement are statements. Statements can create variables as well as initialize them. The if conditional is a condition. The spec does define that a condition can create a variable, but only by = initialization.
Be glad the spec even allows you to create variables in an if at all. It has to have special grammar to even allow that ("condition" being different from "expression"), and that was probably something inherited from C.
That being said, C++11 does define a condition as being able to initialize a declaration with a braced-init-list (spec-speak for "initializer list"). So it should be legal. But since VS2012 doesn't support initializer lists yet...
Why is it that when I define an array of floats like :
const int i[] = { 1, 2, 3, 4 };
float f[i[3]]; // Illegal
outside any function (i.e in global), it is illegal to do so.
while if I do the same thing inside any function (including main() ) like:
void f() {
const int i[] = { 1, 2, 3, 4 };
float f[i[3]];
cout << sizeof(f);
}
main()
{
f();
}
then everything works fine and it outputs the size as 16 successfully.
why is this difference ? Is it because of the change in the storage location from static (initially) to the stack ?
(PS: I know that in C++ an array can not be defined using a variable whose value is not known at the compile time, BUT still, then how is it working in the function ? )
By default, if you don't specify strict standard compliance, compilers often allow C features to be used in C++ (and vice versa). Both GCC and Clang allow such a C99 feature, namely VLAs, to be used by default. (Visual Studio, OTOH, does not support VLAs even in C mode.) Note, however, that VLAs can be defined only in block scope.
6.7.6.2 Array declarators
2 If an identifier is declared as having a variably modified type, it shall be an ordinary
identifier (as defined in 6.2.3), have no linkage, and have either block scope or function
prototype scope. If an identifier is declared to be an object with static or thread storage
duration, it shall not have a variable length array type.
So, a global VLA does not work in C++ (with lax compiler settings) whereas a function local VLA does.
Try compiling the following
$ cat float.cpp
int main() {
int i = 2;
const float f[i] = { 1, 2 };
}
with g++ -std=c++98 -Wall -ansi -pedantic float.cpp and you'd get something like:
float.cpp: In function 'int main()':
float.cpp:3:18: warning: ISO C++ forbids variable length array 'f' [-Wvla]
float.cpp:3:29: error: variable-sized object 'f' may not be initialized
float.cpp:3:15: warning: unused variable 'f' [-Wunused-variable]
C++11 allow this :
#include <iostream>
constexpr int i[] = {1,2,3,4};
float k[i[2]];
int main()
{
std::cout << sizeof(k) << "\n";
}
and it'll be well formed.
As for the initial question, this is probably a G++ extension.
The following code compiles and it seems to run fine:
class Test {
private:
const unsigned MAX;
public:
Test (const unsigned int n) : MAX(n) { }
void foo () {
int array[MAX];
...
}
};
but is it really OK? I mean:
Test a (3);
Test b (8);
does array actually have 3 and 8 cells respectively?
If so, is it because array is an automatic var and gets instantiated with the appropriate dimension?
Thanks
What you have written is valid in c99 but not valid c++.
I am of course talking about your use of VLA's, not the full snippet.
When compiling using g++ -pedantic -ansi -Wall we get the below warning;
foo.cpp: In member function 'void Test::foo()':
foo.cpp:18:23: warning: ISO C++ forbids variable length array 'array' [-Wvla]
As mentioned in the above warning the pattern you are using is often referred to as using a variable length array, which is standard in C99 and "allowed" in C++ through a g++ extension.
I'd recommend you to use a STL container instead of hacks as these, for one single reason; what you are doing is not legal, and therefor isn't guaranteed to be portable cross compilers.
Variable length arrays are not standard C++. You could make Test a template instead:
template <int MAX>
class Test {
public:
Test () {}
void foo () {
int array[MAX];
}
};
Test<4> t4;
Test<8> t8;
You are correct that this is not legal C++. If it works on your compiler, it is probably because you are using a GCC extension.