pre-17 C++ static double

How can I amend the code to make static double work in this context?
I researched and found I could use inline in 17 version of C++.
I'm into pre-17, so i'm asking for your advise.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define SAVINGS_H

class Savings
{
public:
static  double annualInterestRate = 0.04; 

explicit Savings(double );



void calculateMonthlyInterest ();



private:
double savingsBalance; // probably should be static

}; 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <array>
#include <iostream>


#include "Savings.h" 
using namespace std;

string dayChar = "Hello";

Savings::Savings(double balance )
: savingsBalance(balance)
{

} 


void Savings::calculateMonthlyInterest ()
{

savingsBalance = savingsBalance / (annualInterestRate / 12);
cout << savingsBalance << endl;
}



1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include "Savings.h" 

using namespace std;
int main()
{
	
Savings obj1 (1000);
obj1.calculateMonthlyInterest();
} 
Last edited on
In savings.h
1
2
3
4
5
6
7
class Savings
{
    public:
    static const double annualInterestRate ; // declaration

    // ...
};


In savings.cpp
const double Savings::annualInterestRate = 0.04 ; // definition
I got this! Thank you, JLBorges
there is no need for this to be static since it is a constant. Static keeps the current value connected for all instances of the class, but as a constant, they are always the same anyway. No harm to do it, but clutter to make it static.
non-static const member variables are assigned separate memory locations for each instance. static const members use the same memory address for each instance. Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

struct SC {
	const int i {};
};

struct SS {
	const static int i {};
};


int main() {
	SC s1;
	SC s2;

	std::cout << "address s1 " << &(s1.i) << "  address s2 " <<  &(s2.i) << '\n';

	SS s3;
	SS s4;

	std::cout << "address s3 " << &(s3.i) << "  address s4 " << &(s4.i) << '\n';
}


which for me displays:


address s1 00000000002BF7D0  address s2 00000000002BF7D4
address s3 000000013F5C46B0  address s4 000000013F5C46B0


PS. If you want to use constexpr in a struct/class than it needs to be defined as static:

1
2
3
struct CE {
	static constexpr int i {};
};


is OK. But:

1
2
3
struct CE {
	constexpr int i {};
};


is not and doesn't compile.
Last edited on
There has to be a way to have no memory used for a constant, just injected at compile time in place as its value. Something is off there, IMHO, with the optimizer.
I would assume that any decent compiler will "eliminate" a const (or constexpr) variable, regardless of whether it is static or not, and simply "inline" the actual value wherever the const variable is used in the code – provided that the value can already be determined at compile-time. In fact, the compiler may do this with non- const variables just as well, provided that the compiler can deduce that they're "effectively const".

(By printing the address of the const variable, you're probably "forcing" the compiler to not optimize it out)
Last edited on
Except in a trivial situation, storage for const member objects can't be optimised away (by a conforming implementation). Computations on those const members may be performed at compile time (typically on const objects).

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <numeric>
#include <iterator>

struct A
{
    const int c1 = 99 ;
    const int c2[4] { 0, 1, 2, 3 } ;
};

// the object representation of A includes those of the const member objects
static_assert( sizeof(A) >= ( sizeof(const int) * 5 ) ) ;

extern const A ca ;
const A ca ; // const, but storage for ca can't be optimised away (extern)
A ma ; // non-const, storage for ma can't be optimised away

int sum_const()
{
    // compute 99 + 0 + 1 + 2 + 3 + sizeof(A) at compile time; return 125
    return ca.c1 + std::accumulate( std::begin(ca.c2), std::end(ca.c2), 0 ) + sizeof(ca) ;
}

int sum_mutable()
{
    // compute sum of members + sizeof(A) at run time
    return ma.c1 + std::accumulate( std::begin(ma.c2), std::end(ma.c2), 0 ) + sizeof(ma) ;
}

https://gcc.godbolt.org/z/jEKWsGEeE
There has to be a way to have no memory used for a constant, just injected at compile time in place as its value. Something is off there, IMHO, with the optimizer.


constexpr
From what I've heard untested. Constexpr will use different memory addresses across different modules. (I've heard this about static aswell) from my understanding to use minimal amount of resources the Constexpr need to be marked as inline. If I was to make a claim as to no memory usage I would say preprocessor directives namely #define's.
I'm with Kigar .. I think the nosey code of looking at the address of the constant broke it. You would not normally do that.
@jonin... I disagree. I think that might be the case if it the variable was born and died in the same stack frame and wasn't part of an object. The only way to know would be to poke around in the assembly. My assumption is that when an object is instantiated it needs to live somewhere and the compiler isn't going to look at every possible scenario to see if its possible for something to be done with that pointer at runtime. For the example that might seem like an easy task but there's layers and layers of stuff happening behind the scenes that it has to sift though. Even using cout pushes that variable to the iostream library where it needs to decide what to do with it based on the type and if it's a lvalue or an rvalue. << is an operator overload that will make a implicit copy of the rvalue if no temp reference exists. That being said even doing cout<<69; will make a copy of 69 bc if it has no reference it can't travel.
Last edited on
The compiler isn't going to look at every possible scenario to see if its possible for something to be done with that pointer at runtime

This is called "escape analysis"
https://en.wikipedia.org/wiki/Escape_analysis

Clang at least implements it, as it does heap allocation elision.
Last edited on
I had thought about the containment for sure. It's just one of those things to where unless you're only doing relatively simplistic comparisons with a variable its not going anywhere but once you send it into a template library.... its getting sent all over the place. Have you ever looked at a function tree of a program? Even something with only a few lines of code looks damn near incomprehensible. I give those guys credit on their work bc im not sure how they'd ever have time to do anything else. Write something simple compile it and decompile it with ida or equivalent.... the decompiled code will show you if it thinks it's an object or a hard-coded value. It will take you awhile to figure out what you actually wrote strings are usually kinda easy way to determine your code. But you'll still probably bounce around 50-100 functions b4 you find what you wrote.
Well consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

struct SC {
	const int i {1};
};

struct SS {
	const static int i {2};
};

struct SEXP {
	static constexpr int i {3};
};

int main() {
	SC s1, s2;
	SS s3, s4;
	SEXP s5, s6;

	int a = s1.i, b = s2.i, c = s3.i, d = s4.i, e = s5.i, f = s6.i;

	std::cout << "size SC: " << sizeof(SC) << ", size SS: " << sizeof(SS) << ", size SEXP: " << sizeof(SEXP) << '\n';
}


which on my system displays:


size SC: 4, size SS: 1, size SEXP: 1


A static member object does not contribute to the object representation of the class.
Whether it is constexpr or not is immaterial.

Try printing out the size of this:

1
2
3
4
5
6
7
8
struct SEXP {

	static volatile int i ;
	static double d[] ;
};

volatile int SEXP::i = 89 ;
double SEXP::d[1000] {} ;


In practice, even with whole program optimisation, the memory layout of class objects is not optimised. Since strict aliasing is permitted, it is not easy for an optimiser to do very much in this case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

struct SEXP {

	static volatile int i;
	static double d[];
};

volatile int SEXP::i = 89;
double SEXP::d[1000] {};

int main() {
	SEXP s5, s6;

	int e = s5.i, f = s6.i;

	std::cout << "size SEXP: " << sizeof(SEXP) << '\n';
}



size SEXP: 1

Topic archived. No new replies allowed.