템플릿 클래스에서 C ++ 20 클래스 외부 정의


12

C ++의 C ++ 20 표준까지 템플릿 클래스의 개인 멤버를 사용하는 클래스 외 연산자를 정의하려면 다음과 유사한 구문을 사용합니다.

template <typename T>
class Foo;

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);

private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

int main() {
    return 1 == Foo<int>(1) ? 0 : 1;
}

그러나 C ++ 20 이후 클래스 외부 선언을 생략하여 순방향 선언을 생략 할 수 있으므로 다음과 같이 해결할 수 있습니다.

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

Demo

이제 제 질문은 C ++ 20의 어떤 부분에서 그렇게 할 수 있습니까? 그리고 왜 이전 C ++ 표준에서 이것이 가능하지 않았습니까?


주석에서 지적했듯이 clang은 데모에 표시된이 코드를 허용하지 않으므로 실제로 gcc의 버그 일 수 있습니다.

gcc의 bugzilla에 버그 보고서 를 제출했습니다


2
나는 개인적으로 클래스 정의를 선호하고 템플릿 기능 (및 추론 "문제"(과 일치하지 않음 "c string" == Foo<std::string>("foo"))을 피합니다.
Jarod42

@ Jarod42 나는 전적으로 동의합니다. 클래스 내 정의도 선호합니다. C ++ 20이 클래스를 정의 할 때 함수 서명을 세 번 반복 할 수 없다는 것을 알게되어 놀랐습니다.이 구현은 숨겨진 .inl 파일에있는 공용 API에서 유용 할 수 있습니다.
ProXicT

나는 그것이 불가능하다는 것을 알지 못했습니다. 지금까지 문제없이 어떻게 사용 했습니까?
ALX23z

1
temp.friend 에서 흠, 크게 바뀌지 않았으며, 특히이 행동에 책임이있는 1.3 은 아니 었습니다 . 그 소리는 않기 때문에 하지 코드를 받아, 나는 버그를 가진 GCC쪽으로 생각이 기울고 있어요.
n314159

@ ALX23z 클래스가 템플릿 화되지 않은 경우 클래스 외부 선언없이 작동합니다.
ProXicT

답변:


2

GCC에 버그가 있습니다.

<문제의 이름이 (친구, 명시 적 전문화 또는 명시 적 인스턴스화) 선언에서 선언 된 이름 인 경우에도 이름 앞에는 템플릿 이름이 항상 앞에 나타납니다 .

operator==친구 선언 의 이름 은 규정되지 않은 이름이며 템플리트에서 이름을 조회 할 수 있으므로 2 단계 이름 조회 규칙이 적용됩니다. 이와 관련 operator==하여 종속 이름이 아니므로 (함수 호출의 일부가 아니므로 ADL이 적용되지 않음) 이름이 나타나는 지점에서 조회되고 바인딩됩니다 ([temp.nondep] 단락 1 참조). 이 이름 조회에서에 대한 선언이 없으므로 예제가 잘못 구성되었습니다 operator==.

GCC가 P0846R0 으로 인해 C ++ 20 모드에서이를 수락 할 것으로 예상합니다 . 이는 operator==<T>(a, b)템플릿으로의 사전 선언 operator==이 표시 되지 않더라도 (예 :) 템플릿에서 사용할 수 있습니다.

더 흥미로운 테스트 케이스가 있습니다.

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

을 사용 -DWRONG_DECL하여 GCC와 Clang은이 프로그램이 잘못 작성되었다는 데 동의합니다. 템플릿 정의와 관련하여 친구 선언 # 2에 대한 정규화되지 않은 조회는 인스턴스 #의 일치하는 친구와 일치하지 않는 선언 # 1을 찾습니다 Foo<int>. 템플릿의 정규화되지 않은 조회에서 찾을 수 없으므로 선언 # 3도 고려되지 않습니다.

을 사용 -UWRONG_DECL하면 GCC (C ++ 17 및 이전 버전)와 Clang은이 프로그램이 다른 이유로 잘못 구성되어 있음에 동의합니다 operator==.

그러나 -UWRONG_DECLC ++ 20 모드의 GCC는 operator==# 2에서 정규화되지 않은 조회 가 실패한 것 (P0846R0으로 인한 것)이 정상인지 확인한 다음 템플릿 인스턴스화 컨텍스트에서 조회를 다시 실행하여 이제 # 3을 찾습니다. 템플릿에 대한 일반적인 2 단계 이름 조회 규칙 위반


이 자세한 설명에 감사드립니다.
ProXicT
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.