클래스 템플릿의 멤버 함수에 대한 명시 적 인스턴스화 선언이 클래스 템플릿의 인스턴스화를 유발합니까?

Nov 20 2020

[dcl.spec.auto] / 14 상태 [ emphasis mine] :

명시 적 인스턴스화 선언은 자리 표시 자 유형을 사용하여 선언 된 엔티티의 인스턴스화를 유발 하지 않지만 해당 엔티티가 유형을 결정하는 데 필요한대로 인스턴스화되는 것을 방지하지 않습니다. [  예 :

template <typename T> auto f(T t) { return t; }
extern template auto f(int);    // does not instantiate f<int>
int (*p)(int) = f;              // instantiates f<int> to determine its return type, but an explicit
                                // instantiation definition is still required somewhere in the program

 —  최종 예  ]

그리고 [temp.explicit] / 11 상태 [ emphasis mine] :

엔티티 의 대상이 명시 적 인스턴스화 선언 하고이되고 또한 다른 암시 적 인스턴스화 원인이 방법에 사용되는 번역 단위를 프로그램에서 명시 적 인스턴스화 정의 어딘가의 대상이된다; 그렇지 않으면 프로그램의 형식이 잘못되어 진단이 필요하지 않습니다.

이제 다음 프로그램을 고려하십시오.

template <class T>
struct Foo {
    static const auto& foo() { static T t; return t; }
};

// explicit instantiation declarations
extern template const auto& Foo<void>::foo();
extern template const auto& Foo<int>::foo();

int main() {}

이것은 잘 구성되어 있습니다. [temp.explicit] / 11 은 클래스 템플릿 전문화 엔티티의 멤버 함수로 적용되지 않으며 [dcl.spec.auto] / 14 (1)에 따라 암시 적 인스턴스화를 유발하는 방식으로 Foo<void>::foo()Foo<int>::foo()사용 되지 않습니다 .

이제 클래스 템플릿의 friend 선언에서 friend 함수를 정의했는지 생각해보세요 Foo.

template <class T>
struct Foo {
    static const auto& foo() { static T t; return t; }
    friend void bar() { }
};
void bar();

Foo동일한 번역 단위에서 하나 이상의 전문화 가 인스턴스화되면 [basic.def.odr] / 1 이 위반됩니다.

번역 단위는 변수, 함수, 클래스 유형, 열거 유형 또는 템플릿에 대한 정의를 두 개 이상 포함 할 수 없습니다.

친구 가 인스턴스화되는 각 전문화에 대해 bar()재정의 (2) 되기 때문입니다.

위의 인수에 따르면, (클래스 템플릿의) 두 멤버 함수 전문화의 명시 적 인스턴스화 선언은 ( [dcl.spec.auto] / 14에 따라 ) 연관된 클래스 템플릿의 인스턴스화로이어서는 안됩니다. 이는 다음 프로그램을 의미합니다. 또한 틀림없이 올바른 형식이어야합니다.

template <class T>
struct Foo {
    static const auto& foo() { static T t; return t; }
    friend void bar() { }
};
void bar();

extern template const auto& Foo<void>::foo();
extern template const auto& Foo<int>::foo();

int main() {}

그러나 Clang (10.0.0)과 GCC (10.1.0) 모두 "redefinition of void bar()"오류 와 함께 프로그램 (C ++ 14, C ++ 17, C ++ 2a)을 거부합니다 .

그 소리

오류 : 재정의 bar

참고 : Foo<int>여기에서 요청 된 템플릿 클래스의 인스턴스화 :extern template const auto& Foo<int>::foo();

GCC

의 인스턴스화 struct Foo<int>:

오류 : 재정의 void bar()

그러나 나는 Foo<int>또는 Foo<void>전문화가 인스턴스화 되는 방식으로 이러한 전문화를 요청하지 않았습니다 .

따라서 질문 :

  • 위의 프로그램 (친구와 함께)이 잘 구성되어 있습니까? 아니면 컴파일러가 클래스 템플릿 전문화를 인스턴스화하고 이후에 프로그램을 거부 할 수 있습니까?

(1) foo()자리 표시 자 유형을 사용하여 선언되지 않은 경우에도 동일한 질문 (및 컴파일러 동작)이 적용 되지만 [dcl.spec.auto] / 14 의 명시 성으로 대체 할 수는 없지만 필요하지 않을 수도 있습니다.

(2) 친구 선언에 정의 된 친구가 인라인이므로 실제로 다른 번역 단위에서 다른 전문화를 인스턴스화하고 여전히 ODR을 존중할 수 있지만이 논의에서는 관련이 없습니다.

답변

1 DavisHerring Nov 20 2020 at 10:04

클래스 템플릿을 인스턴스화해야한다는 주장은 선언 일치 가 인스턴스화가 필요한 클래스에 대한 정보를 알아야한다는 것입니다. 단순화 된 예를 고려하십시오.

template<class T>
struct A {void f(T) {}};

extern template void A<int>::f(int);

멤버 함수가 존재하는지 확인하려면 클래스 템플릿에서 선언을 인스턴스화해야합니다. 일반적으로 전체 클래스를 인스턴스화하지 않고는이를 수행 할 수 없습니다. 매개 변수 유형은 클래스 템플릿의 다른 선언에 종속 될 수 있습니다. 여러 오버로드를 고려하거나 템플릿 인수 추론을 수행하여 f의미 를 결정해야 합니다. 인스턴스화는 이러한 상황 중 하나가 실제로 관련되어 CWG2 영역 (인스턴스화가 명백히 불가능한 경우)에 해당하는 경우에만 발생해야한다고 주장 할 수 있지만, 이러한 질문에 대해 결정 하려면 원칙적으로 인스턴스화가 필요합니다. 먼저 템플릿 자체를 살펴 보십시오 .