C++ Constants and aggregates inside function body vs. outside - c++

Consider the following piece of code:
#include <iostream>
using namespace std;
int main()
{
int x = 3;
const int i[] = { 1, 2, 3, 4 };
float f[i[3]];
struct S { int i, j; };
const S s[] = { { 1, 2 }, { 3, 4 } };
double d[s[1].j];
}
It runs without error. However, the following:
#include <iostream>
using namespace std;
int x = 3;
const int i[] = { 1, 2, 3, 4 };
float f[i[3]]; // error: array bound is not an integer constant before ']' token|
struct S { int i, j; };
const S s[] = { { 1, 2 }, { 3, 4 } };
double d[s[1].j]; // error: array bound is not an integer constant before ']' token|
int main()
{
return 0;
}
Does not, as it gets the errors that are highlighted as comments. Can anyone explain to me why that is?

More than likely, the reason the compiler allows it within the function is due to a compiler extension: variable-length arrays. They allow arrays declared inside of functions to have non-constexpr lengths. But it only works inside of functions, not at global scope.

You have to use constexpr instead of const
constexpr int i[] = { 1, 2, 3, 4 };
constexpr S s[] = { { 1, 2 }, { 3, 4 } };
const applies for variables, and prevents them from being modified in your code.
constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc.
Reason why it does compile in funcion is VLA. Its not possible to declare VLA in global 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.
Also VLAs are not part of c++ standard, its only compiler extension here.

Related

How to extern const struct with vector members?

I am splitting my code into declarations and definitions. I didn't get any problems until trying to do anything with this constant struct, consisting of vectors. Leaving this code in header leads to multiple definitions type of errors.
// Core.h:
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
I tried moving this code into cpp file and using extern with the struct in the header, but that didn't help. On use cases of struct fields in other files I was getting undeclared identifiers type of errors.
// Core.cpp:
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
// Core.h:
extern const struct ConstData Constants;
Tried putting the struct, with uninitialised fields, before the extern. Thought that might help, so compiler sees with what type of struct it's working and what fields it has. But that is considered redefinition, since I have same struct in cpp file.
// Core.h:
const struct ConstData {
vector<int> numbers1;
vector<int> numbers2;
};
extern const struct ConstData Constants;
// Core.cpp:
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
I'm kinda stuck at this point. Looking up how people are dealing with this issue didn't lead me to much success. In extern docs of Microsoft was said that const modifier changes linkage type (internal or external). So I tried all of the above again, but playing with the const at the same time — no progress on that. Maybe I missed something..
Hope I have supplied enough info and that community will be able to help me!
This code
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
...declares both the structure type ConstData and the variable Constants. You cannot re-declare the structure in the .cpp file if you already declare it in the header file.
You want to split both declarations in the header and only initialize the variable in the .cpp file:
// Core.h
#include <vector>
struct ConstData {
std::vector<int> numbers1;
std::vector<int> numbers2;
};
extern const ConstData Constants;
// Core.cpp
#include <Core.h>
const ConstData Constants{
{ 1, 2, 3, 4, 5 },
{ 0, 10, 20, 30 }
};
I would recommend a struct with static members and std::array for the elements. Since c++17 you can declare this in header file:
#include <array>
struct ConstData {
static constexpr std::array numbers1 = { 1, 2, 3, 4, 5 };
static constexpr std::array numbers2 = { 0, 10, 20, 30 };
};
And use ConstData::number1 / ConstData::number2.
Before c++17 it is a bit more effort:
// Header file:
struct ConstData {
static constexpr std::array<int, 5> numbers1 = { 1, 2, 3, 4, 5 };
static constexpr std::array<int, 5> numbers2 = { 0, 10, 20, 30 };
};
// c++-File
constexpr std::array<int,5> ConstData::numbers1;
constexpr std::array<int,5> ConstData::numbers2;
These are compile-time structures. So they appear 1:1 in memory. Using std::vector always allocates memory and copies the elements at runtime.

Passing global variables as const reference

The following code compiles and works. The value displayed of both a and n are 4.
#include <iostream>
using namespace std;
int a = 2;
void foo(int const&n)
{
a = n*2;
cout<<"a = "<<a<<" n = "<<n<<endl;
}
int main()
{
foo(a);
}
OUTPUT: a = 4 n = 4
Why does the compiler not complain about n being a const reference? For example, the following code fails to compile.
#include <iostream>
using namespace std;
int a = 2;
void foo(int const&a)
{
a = a*2;
cout<<"a = "<<a<<endl;
}
int main()
{
foo(a);
}
OUTPUT: In function 'void foo(const int&)':
10:7: error: assignment of read-only reference 'a'
How are the two cases different ?
In the first case you're assigning to a global variable a. n changes because it's a reference to the mutable global variable. Changing a is allowed, but changing n directly is forbidden.
In the second case you're trying to re-assign to the const argument a. This is forbidden as a is const.
What you've done is shadow global variable a with a local variable. In the second example, within foo the global variable named a does not exist, instead there's an argument that occupies that name.

Redeclare variable inside enum

In C, If we re-declare variable inside enum, then compiler gives an error that "'i' redeclared as different kind of symbol".It Ok.
#include <stdio.h>
int i = 10;
struct S
{
enum
{
i = 20
}e;
};
int main()
{
printf("%d\n", i);
}
But, In C++, If we redeclare variable inside enum, then it's working fine.
#include <iostream>
using namespace std;
int i = 10;
struct S
{
enum
{
i = 20
}e;
};
int main()
{
cout<<i<<endl;
}
I don't understand, Why doesn't C++ compiler gives an error for redeclaration variable?
It doesn't give a re-declaration error because the enumerator is introduced into class scope. Recall that a struct and class are mostly interchangeable in C++. The scope of S contains the enumerator i.
In C however, struct S doesn't define a scope. There are only 4 types of scope in C: function, file, block, and function prototype. As such, i is introduced into file scope where the variable i is already defined.

Range based for loop throws a compiler error with array parameter

Admittedly, I'm just finding my footsteps in C++ but I don't understand the error here.
The following error gets displayed:
Error 1 error C3312: no callable 'begin' function found for type 'int []'
Error 2 error C3312: no callable 'end' function found for type 'int []'
Error 3 error C2065: 'i' : undeclared identifier
4 IntelliSense: this range-based 'for' statement requires a suitable "begin" function and none was found
Code:
#include <iostream>
using namespace std;
void printArray(int[]);
int main() {
int a[] = { 1, 2, 3, 4, 5 };
printArray(a);
system("pause");
return 0;
}
void printArray(int a[]) {
for (int i : a) {
cout << i << " ";
}
}
Can't figure out what the problem is.
Inside printArray, a is not an array! I know it looks like one, but it's not. int a[] there means int* a, due to a nasty legacy from the 1850s.
Here is the fix to your problem, passing in the array by reference and therefore keeping its full type (including numerical dimension):
#include <iostream>
template <size_t N>
void printArray(int (&a)[N]) {
for (int i : a) {
std::cout << i << " ";
}
}
int main() {
int a[] = { 1, 2, 3, 4, 5 };
printArray(a);
}
(I've also removed some redundant code and that horrid system("pause"): properly configure your execution environment rather than having your program take responsibility for blocking its caller when it's finished!)
(live demo)
void printArray(int a[])
Despite that function seeming to accept an array, what it actually gets is a pointer to the first element of said array. This is unfortunately a fact of life in C++, due to it's origins based on the C language.
And, as a pointer itself has no size information about the underlying array, no iterators are available for it, so begin and end will not work.
There are ways to do this with templates but, in this case, I'd probably just bite the bullet and use a vector:
#include <iostream>
#include <vector>
void printArray (const std::vector<int> &a) {
for (int i : a)
std::cout << i << ' ';
std::cout << '\n';
}
int main () {
const std::vector<int> a = { 1, 2, 3, 4, 5 };
printArray (a);
return 0;
}

Initialise static pointers in class

I'm trying to initialise static pointers as arrays inside a class definition, but getting runtime errors. I've seen examples where something similar is done, but I can't get it working, and not sure why. Some example code I've tried is:
class Header
{
private:
static int *pointer;
public:
int getVal(int val);
};
Class definition:
#include "Header.h"
int* Header::pointer = new int[] {0, 1, 2, 3, 4};
int Header::getVal(int val)
{
return pointer[val];
}
main:
#include "Header.h"
#include <iostream>
int main(int argc, char ** argv)
{
Header header;
for (int i = 0; i < 5; i++)
{
std::cout << header.getVal(i);
}
}
Running this causes an error while initialising the pointer. If I run through it in the debugger, and ignore the error, I can see that the pointer is initisalised with 0 at the beginning. If I then continue to step through it I get another error saying the heap's been corrupted. Is it possible to intitialise a pointer in this way? If not, are there any suggestions on how one can initialise a member variable pointer into an array, and assign it values, inside the class definition without having to assign each element of the array individually.
You could probably get away with:
class Header
{
public:
int getVal(int valIndex);
};
and then
#include "Header.h"
static int s_vals[] = {0, 1, 2, 3, 4}; // could move this line to B
int Header::getVal(int valIndex)
{
// B
return s_vals[valIndex];
}
Considering that you know the size of the array at compile time and there is no need to advertise an implementation detail if you are providing accessors anyway.
it is possible that your compiler simply does not support braced-init-list.
If so you can rewrite your class the following way
class Header
{
private:
static int *pointer;
static int *init()
{
int *p = new int[5];
std::iota( p, p + 5, 0 );
return ( p );
}
public:
int getVal(int val);
};
And then pointer is defined the following way
int * Header::pointer = Header::init();