I am trying to understand whether references to array of unknown bound can be used as call parameter in functions in C++. Below is the example that i have:
EXAMPLE 1
void func(int (&a)[])
{
}
int main()
{
cout << "Hello World" << endl;
int k[] = {1,2,3};
// k[0] = 3;
func(k);
return 0;
}
To my surprise this example 1 above works when compiled with GCC 10.1.0 and C++11 but doesn't work with GCC version lower that 10.x. I don't think that we can have references to arrays of unknown size in C++. But then how does this code compile at the following link: successfully compiled
My second question is that can we do this for a function template? For example,
EXAMPLE 2
template<typename T1>
void foo(int (&x0)[])
{
}
Is example 2 valid C++ code in any version like C++17 etc. I saw usage of example 2 in a book where they have int (&x0)[] as a function parameter of a template function.
I don't think that we can have references to arrays of unknown size in C++.
That used to be the case, although it was considered to be a language defect. It has been allowed since C++17.
Note that implicit conversion from array of known bound to array of unknown bound - which is what you do in main of example 1 - wasn't allowed until C++20.
Is example 2 valid C++ code in any version like C++17
Yes; the template has no effect on whether you can have a reference to array of unknown bound.
Related
I came across a source file here (in C). It uses a reasonable-but-strange style to initialize an array. I gave it a try in a shorter C++ program (please notice the "old way" in the code's comment):
arr.cc
#include <iostream>
using namespace std;
int main() {
long arr[] = { [0] = 100, [1] = 101 }; // old way: long arr[] = { 100, 101 };
cout << arr[0] << " " << arr[1] << endl;
}
The code is compiled like this:
g++-6 -std=c++14 arr.cc -o arr
When run, the output is this:
100 101
It passed with -std=c++14? But I can't find it in a C++ reference website, like cppreference.com. Does it conform to the standard? If so, since which version?
This is not valid C++ code; the C standard adopted it, and as an extension GCC allows it in C++ as well. To get GCC to conform to the C++ standard, you need to pass both -std=c++17 (or whatever version) and -pedantic. With that, your code does emit warnings saying it's nonstandard. The description of how this works in GCC is here.
Note that you can also do this with struct members, not just arrays; given
struct Point
{
int x;
int y;
};
you can say Point p = {.x = 17}. This is also nonstandard in C++ so far, but it will be adopted in C++2a. This only applies to the non-array version so far; I don't know if there are plans to add the array version as well or if it will happen by C++2a.
I am slowly bringing myself up to c++11. I was looking at constexpr and stumbled into this wikipedia article which lead me to "something completely different". The basic example it gives is:
int get_five() {return 5;}
int some_value[get_five() + 7]; // Create an array of 12 integers. Ill-formed C++
It states "This was not legal in C++03, because get_five() + 7 is not a constant expression." and says that adding constexpr to the get_five() declaration solves the problem.
My question is "What problem?". I compiled that code with neither errors nor warnings. I played with it making it horribly non constant:
#include <iostream>
int size(int x) { return x; }
int main()
{
int v[size(5) + 5];
std::cout << sizeof(v) + 2 << std::endl;
}
This compiles with no complaints using:
g++ -Wall -std=c++03
and when executed I get the (correct) answer 42.
I admit that I generally use stl containers, not arrays. But I thought (and apparently so did wikipedia) that compilation of the above code would fail miserably. Why did it succeed?
Variable-length arrays (that is, arrays whose size is determined by a non-constant expression) are allowed in C, and some C++ compilers allow them as an extension to the language. GCC is one such compiler.
You'll get a warning if you compile with -pedantic or -Wvla, or an error with -pedantic-errors. Use those flags if you want to avoid non-standard compiler extensions.
As it has been said already some C++ compilers support C feature named Variable Length Array(s) whose sizes can be specified at run-time.
However VLA(s) may not be declared with static storage duration. The program you showed
#include <iostream>
int size(int x) { return x; }
int main()
{
int v[size(5) + 5];
std::cout << sizeof(v) + 2 << std::endl;
return 0;
}
can be compiled. However if you place the array outside any function then the code will not be compiled. Consider the following program that is similar to your original program with minor changes.
#include <iostream>
int size(int x) { return x; }
int v[size(5) + 5];
int main()
{
std::cout << sizeof(v) + 2 << std::endl;
return 0;
}
In this case the compiler will issue an error. However if you will specify constexpr for function size then the above program will be compiled successfully
#include <iostream>
constexpr int size(int x) { return x; }
int v[size(5) + 5];
int main()
{
std::cout << sizeof(v) + 2 << std::endl;
return 0;
}
The C++ Standard requires that sizes of arrays would be constant expressions.
8.3.4 Arrays [dcl.array]
1 In a declaration T D where D has the form
D1 [ constant-expressionopt] attribute-specifier-seqopt
and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type;
Take into acount that not all C++ compilers (and even C compilers; for C compilers it is implementation defined whether a compiler supports VLA) have such a language extension as VLA. So if you want that your program would be C++ compliant then you should not rely on specific language extensions of a compiler.
Some compilers have extensions, which implement VLA (Variable Length Arrays).
Compile with -pedantic and you'll see the difference.
I'm a novice programmer trying to get a head start on some classes before the summer semester starts, and I've run into this problem while trying to create a Quick Union algorithm in C++.
I've been trying to figure out why my program creates two identical arrays, despite having two separate for loops designed to create two different arrays. Whenever my program runs to completion and prints id[] and sz[], it always outputs 1 as the element at every index in both arrays.
class quickUnionUF{
private:
int id[];
int sz[];
int root(int);
public:
quickUnionUF(int, int);
bool connected(int, int);
void unionPoint(int, int);
void print();
};
quickUnionUF::quickUnionUF(int n, int b){
id[n];
sz[b];
for(int i=0;i<n;i++){
id[i] = i;
}
for(int j=0;j<b;j++){
sz[j] = 1;
}
}
For example, if I create quickUnionUF(5, 5);
id[] should now contains elements:
0, 1, 2, 3, 4
And sz[] contains elements:
1, 1, 1, 1, 1
However, the program creates an array sz[] AND array id[] with elements:
1, 1, 1, 1, 1
Any thoughts as to why this is happening?
Standard C++ does not have sizeless array members.
Use std::vector<int> as dynamically sized arrays in C++.
#include <vector>
class quickUnionUF{
private:
std::vector<int> id;
std::vector<int> sz;
int root(int);
public:
quickUnionUF(int, int);
bool connected(int, int);
void unionPoint(int, int);
void print();
};
quickUnionUF::quickUnionUF(int n, int b)
: id(n)
, sz(b)
{
for(int i=0;i<n;i++){
id[i] = i;
}
for(int j=0;j<b;j++){
sz[j] = 1;
}
}
Your code hints at a two very important mistakes:
C++ does not work like Java. int id[] is not an reference to an array of arbitrary size on the garbage collected heap. It is instead a member array of undefined size used to implement dynamic arrays (and similar features) in C99. You should never use this syntax unless you know exactly what you are doing, because it is almost guaranteed to be wrong otherwise.
id[n] does not allocate an array at all. Instead it just indexes id and discards the result.
Listen to your compiler!
First, your code should not compile due to the fact, that only the last member of a struct may be a flexible array type. In fact clang howls:
main.cpp:53:9: error: field has incomplete type 'int []'
int id[];
MSVC howls:
1>main.cpp(54): error C2229: class 'quickUnionUF' has an illegal zero-sized array
And g++ only warns (well, g++ is strange in what it accepts sometimes):
main.cpp:53:12: warning: ISO C++ forbids zero-size array ‘id’ [-Werror=pedantic]
int id[];
Note: g++ is wrong in compiling this, even if one allows flexible array members. This is defined in C99 6.7.2.1§16 and C11 6.7.2.1§18 both of which begin with (emphasis is mine):
As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. [...]
What is happening?
Well, assuming you got your code to compile anyway, it basically means the following:
Create an object with the alignment of integers, but NO elements at all. Take a peek at the following test program:
quickUnionUF q;
::std::cout << sizeof(quickUnionUF) << "\n";
::std::cout << &q << "\n" << &q.id[0] << "\n" << &q.sz[0] << "\n";
The only compiler that managed to compile this at all (gcc 4.9.0) gave the following result:
0
0x7fff1bf6274c
0x7fff1bf6274c
0x7fff1bf6274c
So, this is a zero byte object (yes, this is illegal C++, since every C++ object has a size > 0) and the first element of each array is at the same position (OUTSIDE YOUR OBJECT!). Remember, you declared id and sz to have zero elements!
Therefore, you are writing to the same arbitrary position. You can consider this the extreme case of a buffer overflow: By writing 5 integers to a zero size buffer, you are overflowing from the first zero size buffer through the second zero size buffer into memory totally not under your control.
This also explains your observed result: The second loop simply overwrites what the first did (and it still does it by corrupting your stack).
How do I fix this?
Just use a vector. You can tell it how big you want it and you can ask it to tell you when you are indexing to some position that is not yours.
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.