Programing

상수가 아닌 정적 멤버 또는 클래스의 정적 배열을 초기화 할 수없는 이유는 무엇입니까?

lottogame 2020. 9. 1. 07:57
반응형

상수가 아닌 정적 멤버 또는 클래스의 정적 배열을 초기화 할 수없는 이유는 무엇입니까?


클래스에서 상수가 아닌 static멤버 또는 static배열을 초기화 할 수없는 이유는 무엇 입니까?

class A
{
    static const int a = 3;
    static int b = 3;
    static const int c[2] = { 1, 2 };
    static int d[2] = { 1, 2 };
};

int main()
{
    A a;

    return 0;
}

컴파일러는 다음 오류를 발행합니다.

g++ main.cpp
main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member ‘b’
main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type ‘const int [2]’
main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type ‘int [2]’

두 가지 질문이 있습니다.

  1. static클래스에서 데이터 멤버를 초기화 할 수없는 이유는 무엇 입니까?
  2. static클래스에서 const배열 , 심지어 배열을 초기화 할 수없는 이유는 무엇 입니까?

static클래스에서 데이터 멤버를 초기화 할 수없는 이유는 무엇 입니까?

C ++ 표준에서는 정적 상수 정수 또는 열거 유형 만 클래스 내에서 초기화 할 수 있습니다. 이것이 a다른 것들은 초기화되지 않고 초기화가 허용 되는 이유 입니다.

참조 :
C ++ 03 9.4.2 정적 데이터 멤버
§4

정적 데이터 멤버가 const 정수 또는 const 열거 형인 경우 클래스 정의의 선언은 정수 상수 식 (5.19)이 될 상수 초기화 프로그램을 지정할 수 있습니다. 이 경우 멤버는 정수 상수 식에 나타날 수 있습니다. 멤버는 프로그램에서 사용되는 경우 네임 스페이스 범위에서 정의되어야하며 네임 스페이스 범위 정의에는 이니셜 라이저가 포함되지 않아야합니다.

정수 유형이란 무엇입니까?

C ++ 03 3.9.1 기본 유형
§7

bool, char, wchar_t 유형과 부호있는 정수 유형과 부호없는 정수 유형을 총칭하여 정수 유형이라고합니다 .43) 정수 유형의 동의어는 정수 유형입니다.

각주:

43) 따라서 열거 형 (7.2)은 정수가 아닙니다. 그러나 열거 형은 4.5에 지정된대로 int, unsigned int, long 또는 unsigned long으로 승격 될 수 있습니다.

해결 방법 :

열거 형 트릭사용 하여 클래스 정의 내에서 배열을 초기화 할 수 있습니다 .

class A 
{
    static const int a = 3;
    enum { arrsize = 2 };

    static const int c[arrsize] = { 1, 2 };

};

표준이 이것을 허용하지 않는 이유는 무엇입니까?

Bjarne은 여기에서 이것을 적절하게 설명합니다 .

클래스는 일반적으로 헤더 파일에서 선언되고 헤더 파일은 일반적으로 많은 변환 단위에 포함됩니다. 그러나 복잡한 링커 규칙을 피하기 위해 C ++에서는 모든 개체에 고유 한 정의가 있어야합니다. C ++에서 객체로 메모리에 저장해야하는 엔티티의 클래스 내 정의를 허용하면 해당 규칙이 깨집니다.

static const정수 유형 및 열거 형 클래스 내 초기화가 허용되는 이유는 무엇 입니까?

The answer is hidden in Bjarne's quote read it closely,
"C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects."

Note that only static const integers can be treated as compile time constants. The compiler knows that the integer value will not change anytime and hence it can apply its own magic and apply optimizations, the compiler simply inlines such class members i.e, they are not stored in memory anymore, As the need of being stored in memory is removed, it gives such variables the exception to rule mentioned by Bjarne.

It is noteworthy to note here that even if static const integral values can have In-Class Initialization, taking address of such variables is not allowed. One can take the address of a static member if (and only if) it has an out-of-class definition.This further validates the reasoning above.

enums are allowed this because values of an enumerated type can be used where ints are expected.see citation above


How does this change in C++11?

C++11 relaxes the restriction to certain extent.

C++11 9.4.2 Static data members
§3

If a static data member is of const literal type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

Also, C++11 will allow(§12.6.2.8) a non-static data member to be initialized where it is declared(in its class). This will mean much easy user semantics.

Note that these features have not yet been implemented in latest gcc 4.7, So you might still get compilation errors.


This seems a relict from the old days of simple linkers. You can use static variables in static methods as workaround:

// header.hxx
#include <vector>

class Class {
public:
    static std::vector<int> & replacement_for_initialized_static_non_const_variable() {
        static std::vector<int> Static {42, 0, 1900, 1998};
        return Static;
    }
};

int compilation_unit_a();

and

// compilation_unit_a.cxx
#include "header.hxx"

int compilation_unit_a() {  
    return Class::replacement_for_initialized_static_non_const_variable()[1]++;
}

and

// main.cxx
#include "header.hxx"

#include <iostream>

int main() {
    std::cout
    << compilation_unit_a()
    << Class::replacement_for_initialized_static_non_const_variable()[1]++
    << compilation_unit_a()
    << Class::replacement_for_initialized_static_non_const_variable()[1]++
    << std::endl;
}

build:

g++ -std=gnu++0x -save-temps=obj -c compilation_unit_a.cxx 
g++ -std=gnu++0x -o main main.cxx compilation_unit_a.o

run:

./main

The fact that this works (consistently, even if the class definition is included in different compilation units), shows that the linker today (gcc 4.9.2) is actually smart enough.

Funny: Prints 0123 on arm and 3210 on x86.


I think it's to prevent you from mixing declarations and definitions. (Think about the problems that could occur if you include the file in multiple places.)


static variables are specific to a class . Constructors initialize attributes ESPECIALY for an instance.

참고URL : https://stackoverflow.com/questions/9656941/why-cant-i-initialize-non-const-static-member-or-static-array-in-class

반응형