Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static Cast to access static const class member [duplicate]

Tags:

c++

casting

So yesterday I was looking on SO and could not find an answer to the following. This situation came from some come code I am working with, but here is the MCVE to demonstrate it.

I have a class A defined in A.h that just has one static const in it. I already initialized it in the header.

#ifndef A_H_
#define A_H_
class A {
public:
    static const int test = 5;
    ~A(){};
};


#endif /* A_H_ */

I then have a class B that needs to access the public static const from class A. In this example it will deep copy the value to a vector.

#ifndef B_H_
#define B_H_

#include "A.h"
#include <vector>
#include <iostream>

class B {
private:
    std::vector<int> testVec;

public:
    B(){

        testVec.push_back((const int)A::test);
        //testVec.push_back(static_cast<const int>(A::test)); //Also works
        //testVec.push_back(A::test); //Doesn't work without forward declaration of const int A::test in main or in this header 
        //(Compiler link error: undefined reference to `A::test')  
        std::cout<< testVec.front() << std::endl;
    }

    ~B(){};
};

#endif /* B_H_ */

Then in main I simple call the constructor of class B.

#include "B.h"

int main() {

    B b;

    return 0;
}
//Does the cout from ctor of B and prints 5 to the screen. 

My question is why does a normal cast or static cast allow me to access this static const variable that has not been forward declared. In normal code, I would forward declare the variable or declare it an extern since it has already been defined. What is the reason as to why the casting allows me to access this variable without a forward declaration? (This may seem like a simple question and may have a simple answer, but I am trying to further my knowledge here).

The output from the compiler link error is:

Invoking: Cygwin C++ Linker
g++  -o "S_Test_p1.exe"  ./src/S_Test_p1.o   
./src/S_Test_p1.o:S_Test_p1.cpp:(.rdata$.refptr._ZN1A4testE[.refptr._ZN1A4testE]+0x0): undefined reference to `A::test'
collect2: error: ld returned 1 exit status
make: *** [makefile:47: S_Test_p1.exe] Error 1

My main question here is why the casting works and not that the solution is to define A::test in main or in B.h (which I know works). I understand that would be accepted and proper. The main question is about the unaccepted way, which is casting. Behind the scenes why does casting work for linking?

like image 747
9Breaker Avatar asked Jan 30 '23 09:01

9Breaker


1 Answers

A declaration of a static member within a class like static const int test = 5; is a declaration but not a definition, even if it has an initializer. A declaration should normally have a corresponding definition. This definition looks like const int A::test; (which is not a "forward declaration".)

However, there's an additional rule that a static const class member of integer type does not need to be defined if only its value is used, its address is not taken, and no reference binds to it (which would be similar to taking its address).

The function you're calling is void std::vector<int>::push_back(const int&);. So passing A::test directly will bind the function parameter reference directly to the object, and require a definition.

On the other hand, if you pass (const int)A::test or static_cast<const int>(A::test), this forces a temporary int value to be created using the value of A::test, and the reference binds to that temporary instead. So in that case, a definition of A::test is not necessary.

Note in C++17, the definition of A::test won't be necessary in any case, since a static class member with an initializer in the class definition is implicitly an inline variable and a definition.

Until then, be sure to define all your class static members in some *.cpp file, in case you do use them in a way that requires a definition.

like image 65
aschepler Avatar answered Feb 05 '23 17:02

aschepler