Initialize constexpr with extern constant - c++

I would like to initialize a constexpr variable with another variable, made visible by extern:
test.cpp
#include "test.h"
int const a[] = {1};
int const d = 4;
test.h
extern int const a[];
extern int const d;
main.cpp
#include "test.h"
int main(void) {
constexpr const int * b = a;
constexpr const int e = d;
}
(Only) The second line in main gives the following error:
error: the value of 'd' is not usable in a constant expression and
test.h:3:18: note: 'd' was not initialized with a constant expression
Could somebody help me to understand, why only one of the two examples fails?

Related

Why this constexpr expression gives me an error?

In the code below, constexpr for the line 2 does not give an error, but line 1 does.
#include <iostream>
using namespace std;
class ComplexNum{
public:constexpr ComplexNum(int _r=0,int _i=0):r(_r),i(_i){}
private:
int r,i;
};
int randGen(){
return 10;
}
constexpr int numGen(int i,int j){
return i+j;
}
int main()
{
constexpr int i=10,j=20;
constexpr ComplexNum c3(randGen(),randGen()); //line 1
constexpr ComplexNum c4(numGen(i,j),numGen(i,j));//line 2
return 0;
}
From the knowledge I have, constexpr evaluates the expression at compile time.
Then, shouldn't the compiler be able to evaluate the expression in line 1, because it returns a constant integer (10 in this case)?. If not, how will line 2 be okay?
The compiler can't compile line one because randGen() is not constexpr. The compiler can't magically tell if a function is constexpr. Maybe it looks constexpr, but you actually want it to run at runtime. For that reason, the compiler doesn't evaluate expressions which are not marked constexpr explicitly. Do this:
#include <iostream>
using namespace std;
class ComplexNum{
public:constexpr ComplexNum(int _r=0,int _i=0):r(_r),i(_i){}
private:
int r,i;
};
constexpr int randGen(){
return 10;
}
constexpr int numGen(int i,int j){
return i+j;
}
int main()
{
constexpr int i=10,j=20;
constexpr ComplexNum c3(randGen(),randGen()); //line 1
constexpr ComplexNum c4(numGen(i,j),numGen(i,j));//line 2
return 0;
}

static variable followed by extern in the same file

The following piece of code compiles as well as executes fine.
What exactly does the extern int a statement mean after static int a.
Note that If i write static int a after extern int a, the compiler throws error as tests.cpp:6: error: a was declared extern and later static
#include<iostream>
//#include "testh.h"
using namespace std;
static int a;
extern int a;
int main()
{
int a;
a=3;
cout<<a<<endl;
cout<<::a<<endl;
return 0;
}
You can declare a variable static then extern, but not extern then static. The result is that the global a still has internal linkage. There is a very similar example (using b as the variable name) in the language standard doc, section [dcl.stc], that states this.

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.

C++ How to share constants with extern between cpp - Error: storage class specified

I've the header file in which I declare some constants with extern:
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include <string>
class Constants
{
public:
Constants(){}
extern const std::string DELIMITER;
extern const std::string FILENAME;
extern const int BLUCAR;
extern const int REDCAR;
extern const int EMPTY;
extern const int SPARSE_LIM;
protected:
private:
};
#endif // CONSTANTS_H
Then in the source I define them like this:
#include "constants.h"
extern const std::string DELIMITER = ",";
extern const std::string FILENAME = "cars.csv";
extern const int BLUCAR = 1;
extern const int REDCAR = 2;
extern const int EMPTY = 0;
extern const int SPARSE_LIM = 5;
Why the compiler gives me Error: storage class specified for 'DELIMITER'?
Firstly, these don't seem to be class members. The way you're using extern it looks like you intended for these to be free. Perhaps in a namespace. Take them out of that class.
Then, when you define them, leave out the extern.
In this context it means "find this variable elsewhere". You don't want to find it elsewhere. It's here!
// Header
namespace Constants {
extern const std::string DELIMITER;
extern const std::string FILENAME;
extern const int BLUCAR;
extern const int REDCAR;
extern const int EMPTY;
extern const int SPARSE_LIM;
}
// Source
namespace Constants {
const std::string DELIMITER = ",";
const std::string FILENAME = "cars.csv";
const int BLUCAR = 1;
const int REDCAR = 2;
const int EMPTY = 0;
const int SPARSE_LIM = 5;
}
Remember, you'd do the same for static object definitions!
I think you either want to use static members in your class, use a namespace instead of a class, or put your constants in the global namespace.
For static members:
class Constants
{
public:
Constants(){}
static const std::string DELIMITER;
static const std::string FILENAME;
static const int BLUCAR;
static const int REDCAR;
static const int EMPTY;
static const int SPARSE_LIM;
protected:
private:
};
and in your source file
const std::string Constants::DELIMITER = ",";
const std::string Constants::FILENAME = "cars.csv";
const int Constants::BLUCAR = 1;
const int Constants::REDCAR = 2;
const int Constants::EMPTY = 0;
const int Constants::SPARSE_LIM = 5;
However, it looks like your class is more like a namespace than a class, since there would be no point in making instances.
But there is another option. You may not even want to use a class or a namespace, but just have them all in the global namespace:
// header
extern const std::string DELIMITER;
extern const std::string FILENAME;
extern const int BLUCAR;
extern const int REDCAR;
extern const int EMPTY;
extern const int SPARSE_LIM;
// source
const std::string DELIMITER = ",";
const std::string FILENAME = "cars.csv";
const int BLUCAR = 1;
const int REDCAR = 2;
const int EMPTY = 0;
const int SPARSE_LIM = 5;

C++ where to initialize static const

I have a class
class foo {
public:
foo();
foo( int );
private:
static const string s;
};
Where is the best place to initialize the string s in the source file?
Anywhere in one compilation unit (usually a .cpp file) would do:
foo.h
class foo {
static const string s; // Can never be initialized here.
static const char* cs; // Same with C strings.
static const int i = 3; // Integral types can be initialized here (*)...
static const int j; // ... OR in cpp.
};
foo.cpp
#include "foo.h"
const string foo::s = "foo string";
const char* foo::cs = "foo C string";
// No definition for i. (*)
const int foo::j = 4;
(*) According to the standards you must define i outside of the class definition (like j is) if it is used in code other than just integral constant expressions. See David's comment below for details.
Since C++17 the inline specifier also applies to variables. You can now define static member variables in the class definition:
#include <string>
class foo {
public:
foo();
foo( int );
private:
inline static const std::string s { "foo" };
};
In a translation unit within the same namespace, usually at the top:
// foo.h
struct foo
{
static const std::string s;
};
// foo.cpp
const std::string foo::s = "thingadongdong"; // this is where it lives
// bar.h
namespace baz
{
struct bar
{
static const float f;
};
}
// bar.cpp
namespace baz
{
const float bar::f = 3.1415926535;
}
Static members need to be initialized in a .cpp translation unit at file scope or in the appropriate namespace:
const string foo::s( "my foo");
Only integral values (e.g., static const int ARRAYSIZE) are initialized in header file because they are usually used in class header to define something such as the size of an array. Non-integral values are initialized in implementation file.