Leedehai
Leedehai

Reputation: 3950

C++: (NOT duplicate) integral static member initialized (not just declared!) inside a class causes linker error, why?

EDIT: I'm not asking how to make the code work, but why it has error. Also, the static member is not only declared inside the class, but also has been initialized with a value (at least it appears to have), hence I don't think it's redundant. Also, this question asks beyond that, so please don't close it. It is not a duplicate.

Say class A has a static member data defined inside. The shell record below does not link (ld: undefined reference).

Note that since C++11, std::vector<>::push_back() accepts either a const lvalue reference (const value_type&) or an rvalue reference (value_type&& val). Moreover, even if it doesn't accept an rvalue reference, an rvalue can be bound to a const lvalue reference.

$ cat class.h
struct A {
    static const int STAT_MEMBER = 1;
    A();
    int a;
};

$ cat class.cc
#include "class.h"
#include <vector>
using namespace std;
A::A() { a = STAT_MEMBER; }

int main() {
    vector<int> v;
    v.push_back(A::STAT_MEMBER);
}

$ g++ -std=c++14 class.cc -o class
/tmp/ccNXDnyu.o: In function `main':
class.cc:(.text+0x2f): undefined reference to `A::STAT_MEMBER'
collect2: error: ld returned 1 exit status

$

Question:

(1) is A::STATIC_MEMBER an rvalue? (but regardless of it being an rvalue or an lvalue, it shouldn't cause an error in linker -- it should have been fine or have caused an error in compiler)

(2) why didn't the compiler/linker complain about STATIC_MEMBER in A::A()?

(3) why didn't the compiler complain about STATIC_MEMBER in main()?

(4) why did the linker complain about STATIC_MEMBER in main()?

I know this problem should be gone if the static member is declared inside the class but defined outside the class, but I'm mostly interested the problem above.

G++ version: g++-6 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04) 6.3.0 20170519

Upvotes: 2

Views: 106

Answers (3)

user7860670
user7860670

Reputation: 37548

This can be simplified to this:

struct A { static const int s = 1; };

int main()
{
    int a1 = A::s; // OK
    int const & a2 = A::s; // error, 
    int const * a3 = &A::s; // same thing
}

a1 case works because of the special treatment that integral constants receive. Basically compiler knows the value of s at compile time and does not need to access storage. This also allows s to be used as template parameters:

::std::array<int,A::s> xx; 

Upvotes: 1

Klaus
Klaus

Reputation: 25613

The reason is quite easy:

If you use a value, the compiler can use the known value to your undefined but declared static const member.

But the vector::push_back takes the reference to the value! The reference is the address and there is no address of an undefined object, only the value of the object is known.

You can reduce the code to this, which simplifies it a bit:

struct A {
    static const int STAT_MEMBER = 1;
};

void Works( const int i ){} // this takes the VALUE of the variable

void Fail( const int& i){} // this takes the ADDRESS of the variable
                           // but there is no object which can be 
                           // addressed, because it is not defined!

int main()
{
    Works( A::STAT_MEMBER );
    Fail( A::STAT_MEMBER );
}

Simply comment in and out to get linker error or not!

Upvotes: 2

YePhIcK
YePhIcK

Reputation: 5856

This only works in C++17, so just wait a little bit (or turn on the C++17 support flag in your compiler).

From the cppreference.com:

A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:

struct X
{
    inline static int n = 1;
};

Upvotes: 1

Related Questions