C ++ 11은 비 정적 및 비상 수 멤버의 클래스 내 초기화를 허용합니다. 무엇이 바뀌 었습니까?


87

C ++ 11 이전에는 정수 또는 열거 형의 정적 const 멤버에 대해서만 클래스 내 초기화를 수행 할 수있었습니다. Stroustrup은 C ++ FAQ에서 이에 대해 설명 하며 다음 예제를 제공합니다.

class Y {
  const int c3 = 7;           // error: not static
  static int c4 = 7;          // error: not const
  static const float c5 = 7;  // error: not integral
};

그리고 다음과 같은 추론 :

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

그러나 C ++ 11은 이러한 제한을 완화하여 비 정적 멤버의 클래스 내 초기화를 허용합니다 (§12.6.2 / 8).

비 위임 생성자에서 지정된 비 정적 데이터 멤버 또는 기본 클래스가 mem-initializer-id에 의해 지정되지 않은 경우 ( 생성자에 ctor-initializer 가 없기 때문에 mem-initializer-list 가없는 경우 포함 ) 엔티티가 추상 클래스 (10.4)의 가상 기본 클래스가 아닌 경우

  • 엔티티가 brace-or-equal-initializer 가있는 비 정적 데이터 멤버 인 경우 엔티티는 8.5에 지정된대로 초기화됩니다.
  • 그렇지 않으면 엔티티가 변형 멤버 (9.5)이면 초기화가 수행되지 않습니다.
  • 그렇지 않으면 엔티티가 기본으로 초기화됩니다 (8.5).

섹션 9.4.2는 또한 constexpr지정자 로 표시된 경우 상수가 아닌 정적 멤버의 클래스 내 초기화를 허용 합니다.

그렇다면 C ++ 03에서 제한 한 이유는 어떻게 되었습니까? 단순히 "복잡한 링커 규칙"을 받아들이거나이를 구현하기 쉽게 만드는 다른 변경 사항이 있습니까?


5
아무 일도하지. 컴파일러는 이러한 모든 헤더 전용 템플릿으로 더 똑똑해 졌으므로 이제 비교적 쉽게 확장 할 수 있습니다.
Öö Tiib

C ++ 11 이전 컴파일을 선택할 때 IDE에서 흥미롭게도 비 정적 const 정수 멤버를 초기화 할 수 있습니다.
Dean P

답변:


67

짧은 대답은 컴파일러를 이전보다 더 복잡하게 만드는 대신 링커를 거의 동일하게 유지한다는 것입니다.

즉, 링커가 정렬 할 여러 정의를 생성하는 대신 여전히 하나의 정의 만 생성되며 컴파일러는이를 정렬해야합니다.

그것은 또한 프로그래머 가 분류를 유지하기 위해 다소 복잡한 규칙 으로 이어지지 만, 그것은 큰 문제가 아닐 정도로 충분히 간단합니다. 단일 멤버에 대해 두 개의 다른 이니셜 라이저를 지정하면 추가 규칙이 적용됩니다.

class X { 
    int a = 1234;
public:
    X() = default;
    X(int z) : a(z) {}
};

이제이 시점의 추가 규칙은 a기본이 아닌 생성자를 사용할 때 초기화하는 데 사용되는 값을 처리 합니다. 이에 대한 대답은 매우 간단합니다. 다른 값을 지정하지 않는 생성자를 1234사용하면는 초기화에 사용 a되지만 다른 값을 지정하는 생성자를 사용하면 1234기본적으로 무시됩니다.

예를 들면 :

#include <iostream>

class X { 
    int a = 1234;
public:
    X() = default;
    X(int z) : a(z) {}

    friend std::ostream &operator<<(std::ostream &os, X const &x) { 
        return os << x.a;
    }
};

int main() { 
    X x;
    X y{5678};

    std::cout << x << "\n" << y;
    return 0;
}

결과:

1234
5678

1
이것은 이전에 꽤 가능했던 것 같습니다. 컴파일러 작성 작업이 더 어려워졌습니다. 공정한 진술입니까?
allyourcode 2013-08-28

10
@allyourcode : 예, 아니오. 예, 컴파일러 작성이 더 어려워졌습니다. 하지만, 그것 때문에 는 C ++ 사양 꽤 열심히 작성했다.하지
Jerry Coffin

클래스 멤버를 초기화하는 방법에 차이가 있습니까? int x = 7; 또는 int x {7} ;?
mbaros

9

템플릿이 완성되기 전에 추론이 작성되었을 수도 있습니다. 정적 멤버의 클래스 내 이니셜 라이저에 필요한 모든 "복잡한 링커 규칙"은 템플릿의 정적 멤버를 지원하기 위해 C ++ 11에 이미 필요했습니다.

중히 여기다

struct A { static int s = ::ComputeSomething(); }; // NOTE: This isn't even allowed,
                                                   // thanks @Kapil for pointing that out

// vs.

template <class T>
struct B { static int s; }

template <class T>
int B<T>::s = ::ComputeSomething();

// or

template <class T>
void Foo()
{
    static int s = ::ComputeSomething();
    s++;
    std::cout << s << "\n";
}

컴파일러의 문제는 세 가지 경우 모두 동일합니다. 어떤 번역 단위에서 정의 s를 내보내고 초기화하는 데 필요한 코드입니까? 간단한 해결책은 그것을 어디에서나 내보내고 링커가 그것을 분류하도록하는 것입니다. 링커가 이미 __declspec(selectany). 그것 없이는 C ++ 03을 구현하는 것이 불가능했을 것입니다. 이것이 링커를 확장 할 필요가없는 이유입니다.

좀 더 직설적으로 말하면, 나는 이전 표준에서 주어진 추론이 명백히 잘못되었다고 생각합니다.


최신 정보

Kapil이 지적했듯이 내 첫 번째 예제는 현재 표준 (C ++ 14)에서도 허용되지 않습니다. IMO가 구현 (컴파일러, 링커)에 가장 어려운 경우이기 때문에 어쨌든 그대로 두었습니다. 내 요점은 : 경우 에도 템플릿을 사용할 때 이미 허용 된 것보다 더 어렵지 않습니다.


많은 C ++ 11 기능이 컴파일러에 필요한 기능이나 최적화가 이미 포함되어 있다는 점에서 유사하기 때문에 이것이 어떤 찬성도 얻지 못했습니다.
Alex Court

@AlexCourt 최근에이 답변을 썼습니다. 질문과 Jerry의 대답은 2012 년부터입니다. 그래서 제 답변이별로 주목받지 못한 것 같아요.
Paul Groke

1
정적 const 만 클래스에서 초기화 될 수 있기 때문에 "struct A {static int s = :: ComputeSomething ();}"을
준수하지 않습니다

8

이론상 So why do these inconvenient restrictions exist?...이유는 유효하지만 쉽게 우회 할 수 있으며 이것이 C ++ 11이하는 일입니다.

파일 을 포함 하면 파일 만 포함되고 초기화는 무시됩니다. 멤버는 클래스 를 인스턴스화 할 때만 초기화됩니다 .

즉, 초기화는 여전히 생성자와 연결되어 있으며 표기법 만 다르고 더 편리합니다. 생성자가 호출되지 않으면 값이 초기화되지 않습니다.

생성자가 호출되면 값이 클래스 내 초기화 (있는 경우)로 초기화되거나 생성자가 자체 초기화로이를 재정의 할 수 있습니다. 초기화 경로는 본질적으로 동일합니다. 즉, 생성자를 통해 이루어집니다.

이것은 C ++ 11에 대한 Stroustrup 자체 FAQ 에서 분명 합니다.


Re "생성자가 호출되지 않으면 값이 초기화되지 않습니다.": Y::c3질문에서의 멤버 초기화를 어떻게 피할 수 있습니까? 내가 이해했듯이 c3선언에 제공된 기본값을 재정의하는 생성자가 없으면 항상 초기화됩니다.
피터 - 분석 재개 모니카
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.