문제

정적 멤버 초기화의 경우 중첩 헬퍼 구조물을 사용하여 비 템플릿 클래스에 적합합니다. 그러나, 동봉 클래스가 템플릿에 의해 매개 변수화되면, 기본 코드에서 도우미 객체에 액세스하지 않으면 중첩 초기화 클래스가 인스턴스화되지 않습니다. 예를 들어 단순화 된 예제 (제 경우에는 벡터를 초기화해야 함).

#include <string>
#include <iostream>

struct A
{
    struct InitHelper
    {
        InitHelper()
        {
            A::mA = "Hello, I'm A.";
        }
    };
    static std::string mA;
    static InitHelper mInit;

    static const std::string& getA(){ return mA; }
};
std::string A::mA;
A::InitHelper A::mInit;


template<class T>
struct B
{
    struct InitHelper
    {
        InitHelper()
        {
            B<T>::mB = "Hello, I'm B."; // [3]
        }
    };
    static std::string mB;
    static InitHelper mInit;

    static const std::string& getB() { return mB; }
    static InitHelper& getHelper(){ return mInit; }
};
template<class T>
std::string B<T>::mB; //[4]
template<class T>
typename B<T>::InitHelper B<T>::mInit;


int main(int argc, char* argv[])
{
    std::cout << "A = " << A::getA() << std::endl;

//    std::cout << "B = " << B<int>::getB() << std::endl; // [1]
//    B<int>::getHelper();    // [2]
}

G ++ 4.4.1 :

  • 1] 및 [2]는 다음과 같이 언급했다.

    A = Hello, I'm A.

    의도 한대로 작동합니다

  • 1] 타당한 :

    A = Hello, I'm A.
    B = 

    inithelper가 MB를 초기화 할 것으로 기대합니다

  • 1] 및 [2] 타당한 :
    A = Hello, I'm A.
    B = Hello, I'm B.
    의도 한대로 작동합니다
  • 1] 댓글, [2] 무의미한 :
    3]의 정적 초기화 단계에서의 Segfault

따라서 내 질문 : 이것은 컴파일러 버그입니까, 아니면 버그가 모니터와 의자 사이에 앉아 있습니까? 그리고 후자가 그 사실이라면 : 우아한 해결책이 있습니까 (즉, 정적 초기화 방법을 명시 적으로 호출하지 않고)?

업데이트 I :
이것은 원하는 동작으로 보인다 (ISO/IEC C ++ 2003 표준, 14.7.1에 정의 된 바와 같이) :

클래스 템플릿 또는 멤버 템플릿의 구성원이 명시 적으로 인스턴스화되거나 명시 적으로 전문화되지 않는 한, 멤버 정의가 존재 해야하는 컨텍스트에서 전문화가 참조 될 때 멤버의 전문화가 암시 적으로 인스턴스화됩니다. 특히, 정적 데이터 멤버 자체가 정적 데이터 구성원의 정의가 존재하도록 요구하는 방식으로 자체를 사용하지 않는 한 정적 데이터 구성원의 초기화 (및 관련 부작용)는 발생하지 않습니다.

도움이 되었습니까?

해결책

이것은 얼마 전에 Usenet에서 논의되었으며, 나는 stackoverflow에서 또 다른 질문에 대답하려고했습니다. 정적 데이터 구성원의 인스턴스화 지점. 테스트 사례를 줄이고 각 시나리오를 고립하여 고려할 가치가 있다고 생각합니다.


struct C { C(int n) { printf("%d\n", n); } };

template<int N>
struct A {
  static C c;
}; 

template<int N>
C A<N>::c(N); 

A<1> a; // implicit instantiation of A<1> and 2
A<2> b;

정적 데이터 멤버 템플릿의 정의가 있습니다. 이것은 아직 데이터 구성원을 만들지 않습니다. 14.7.1:

"... 특히 정적 데이터 멤버 자체가 정적 데이터 멤버의 정의가 존재하도록 요구하는 방식으로 사용되지 않는 한 정적 데이터 구성원의 초기화 (및 관련 부작용)는 발생하지 않습니다."

해당 단어를 정의하는 한 정의 규칙에 따라 해당 엔티티가 "사용"할 때 무언가 (= 엔티티)의 정의가 필요합니다. 3.2/2). 특히, 모든 참조가 끊임없는 템플릿에서 나온 경우 템플릿 또는 sizeof 표현식 또는 유사한 것들은 엔티티를 "사용"하지 않는 것입니다 (잠재적으로 평가하지 않거나 아직 사용되는 함수/멤버 함수로 아직 존재하지 않기 때문에 그러한 정적 데이터 구성원은 인스턴스화되지 않습니다. .

암시 적 인스턴스화 14.7.1/7 정적 데이터 구성원의 선언을 인스턴스화합니다. 즉, 해당 선언을 처리하는 데 필요한 템플릿을 인스턴스화합니다. 그러나 즉각적인 정의는 아닙니다. 즉, 이니셜 라이더는 인스턴스화되지 않으며 해당 정적 데이터 구성원의 유형의 생성자는 암시 적으로 정의되지 않습니다 (사용 된 것으로 표시).

즉, 위의 코드는 아직 아무것도 출력하지 않을 것입니다. 지금 정적 데이터 구성원의 암시 적 인스턴스화를 일으 봅시다.

int main() { 
  A<1>::c; // reference them
  A<2>::c; 
}

이로 인해 두 정적 데이터 구성원이 존재하게되지만 문제는 - 초기화 순서는 어떻게됩니까? 간단한 독서에서는 그렇게 생각할 수 있습니다 3.6.2/1 (나에 의한 강조) : 적용 : :

"네임 스페이스 범위로 정의 된 정적 스토리지 지속 시간이있는 객체 번역 장치 동적으로 초기화 된 것은 해당 정의가 번역 장치에 나타나는 순서로 초기화되어야한다. "

이제 USENET 게시물에서 말했듯이 설명했습니다 이 결함 보고서에서,이 정적 데이터 구성원은 번역 장치에 정의되지 않지만 인스턴스화 장치, 설명 된 바와 같이 2.1/1:

각 번역 된 번역 장치는 필요한 인스턴스화 목록을 생성하도록 검사됩니다. [참고 : 여기에는 명시 적으로 요청 된 인스턴스화가 포함될 수 있습니다 (14.7.2). ] 필요한 템플릿의 정의가 있습니다. 이러한 정의를 포함하는 번역 장치의 소스를 사용할 수 있는지 여부는 구현됩니다. [참고 : 구현은 소스가 여기에서 필요하지 않도록 충분한 정보를 번역 된 번역 장치로 인코딩 할 수 있습니다. ] 모든 필요한 인스턴스화는 인스턴스화 단위를 생성하기 위해 수행됩니다. [참고 : 이들은 번역 된 번역 단위와 유사하지만 끊임없는 템플릿 및 템플릿 정의에 대한 참조는 포함되어 있지 않습니다. ] 인스턴스티브가 실패하면 프로그램이 잘못 형성됩니다.

그러한 인스턴스화 지점은 인스턴스화와 해당 번역 단위 사이의 문맥 연결이기 때문에 그러한 회원의 인스턴스화의 요점은 실제로 중요하지 않습니다. 14.6.4.1, 그리고 각각의 인스턴스화 지점은 인스턴스베이션에 하나의 정의 규칙에 지정된 것과 동일한 의미를 부여해야합니다. 3.2/5, 마지막 총알).

우리가 순서 초기화를 원한다면, 우리는 인스턴스화를 엉망으로 만들지 않고 명시적인 선언을 마련해야합니다. 이것은 명시 적 전문 분야의 영역입니다. 이것은 정상 선언과는 크게 다르지 않기 때문입니다. 실제로 C ++ 0X는 3.6.2 다음에 :

정적 저장 시간을 갖는 비 국소 객체의 동적 초기화는 주문되거나 순서가 변하지 않습니다. 명시 적으로 전문화 된 클래스 템플릿의 정의 정적 데이터 구성원이 초기화를 주문했습니다. 다른 클래스 템플릿 정적 데이터 멤버 (즉, 암시 적 또는 명시 적으로 인스턴스화 된 전문화)는 정렬되지 않은 초기화를 가지고 있습니다.


이것은 코드를 의미합니다.

  • [1] 그리고 [2] 주석 : 정적 데이터 구성원에 대한 언급이 없으므로 해당 정의가 존재하지 않기 때문에 인스턴스화가 필요하지 않기 때문에 선언이 아닙니다. B<int>) 인스턴스화되지 않습니다. 부작용이 발생하지 않습니다.
  • [1] 무관 : B<int>::getB() 그 자체로 사용되는 사용됩니다 B<int>::mB, 해당 정적 멤버가 존재해야합니다. 문자열은 메인 이전에 초기화됩니다 (해당 명령문 이전의 경우, 비 국한 객체를 초기화하는 일부). 사용하는 것이 없습니다 B<int>::mInit, 그래서 그것은 인스턴스화되지 않았으므로 B<int>::InitHelper 생성자가 사용되지 않도록 만들어지면서 무언가를 할당하지 않을 것입니다. B<int>::mB: 빈 문자열을 출력합니다.
  • [1] 그리고 [2] 타의 추종을 불허하는 : 이것이 당신에게 효과가 있다는 것은 행운입니다 (또는 반대입니다 :)). 위에서 설명한대로 특정 초기화 호출 순서에 대한 요구 사항은 없습니다. VC ++에서 작동하고 GCC에서 실패하고 Clang에서 작업 할 수 있습니다. 우리는 모른다.
  • [1] 댓글 [2] 무능력 : 같은 문제 - 다시, 둘 다 정적 데이터 구성원입니다 사용된: B<int>::mInit 사용합니다 B<int>::getHelper, 그리고 인스턴스화 B<int>::mInit 생성자가 인스턴스화되며 사용됩니다. B<int>::mB -하지만 컴파일러의 경우이 특정 실행에서 순서가 다릅니다 (지정되지 않은 동작은 다른 실행마다 일관성이 없어야합니다) : 초기화됩니다. B<int>::mInit 먼저, 제조되지 않은 문자열 객체에서 작동합니다.

다른 팁

문제는 정적 멤버 변수에 대해주는 정의도 템플릿이라는 것입니다.

template<class T>
std::string B<T>::mB;
template<class T>
typename B<T>::InitHelper B<T>::mInit;

편집하는 동안 T는 알려지지 않았기 때문에 실제로는 아무것도 정의하지 않습니다. 클래스 선언 또는 템플릿 정의와 같은 것이므로 컴파일러는 볼 때 코드 또는 예약 저장소를 생성하지 않습니다.

정의는 템플릿 클래스를 사용할 때 나중에 암시 적으로 발생합니다. Segfaulting의 경우 사용하지 않기 때문에 bu003Cint> :: Minit, 그것은 결코 만들어지지 않습니다.

솔루션은 필요한 멤버를 명시 적으로 정의하는 것입니다 (초기화하지 않고) : 소스 파일을 어딘가에 넣습니다.

template<>
typename B<int>::InitHelper B<int>::mInit;

이것은 기본적으로 템플릿 클래스를 명시 적으로 정의하는 것과 같은 방식으로 작동합니다.

  • 1] 무관 한 사례 : 괜찮습니다. static InitHelper B<int>::mInit 존재하지 않는다. 템플릿 클래스 (struct)의 멤버가 사용되지 않으면 컴파일되지 않습니다.

  • 1] 및 [2] 무의미한 사례 : 괜찮습니다. B<int>::getHelper() 사용 static InitHelper B<int>::mInit 그리고 mInit 존재합니다.

  • 1]은 언급했다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top