pre-17 C++ static double

Dec 30, 2021 at 9:34am
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 Dec 30, 2021 at 9:58am
Dec 30, 2021 at 10:15am
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
Dec 30, 2021 at 2:05pm
I got this! Thank you, JLBorges
Dec 30, 2021 at 2:28pm
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.
Dec 30, 2021 at 4:42pm
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 Dec 30, 2021 at 4:48pm
Dec 30, 2021 at 10:29pm
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.
Dec 31, 2021 at 12:22am
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 Dec 31, 2021 at 12:32am
Dec 31, 2021 at 3:58am
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
Dec 31, 2021 at 10:32am
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
Dec 31, 2021 at 2:02pm
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.
Dec 31, 2021 at 3:21pm
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.
Dec 31, 2021 at 5:24pm
@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 Dec 31, 2021 at 5:26pm
Dec 31, 2021 at 6:57pm
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 Dec 31, 2021 at 7:01pm
Dec 31, 2021 at 8:43pm
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.
Jan 1, 2022 at 10:59am
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


Jan 1, 2022 at 11:15am
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.
Jan 1, 2022 at 11:22am
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.