Aż do standardu C ++ 20 C ++, gdy chcieliśmy zdefiniować operatora spoza klasy, który korzysta z niektórych prywatnych członków klasy szablonu, używamy konstrukcji podobnej do tej:
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;
}
Od wersji C ++ 20 możemy jednak pominąć deklarację spoza klasy, a więc także deklarację forward, dzięki czemu możemy uniknąć:
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;
}
Moje pytanie brzmi: jaka część C ++ 20 pozwala nam to zrobić? I dlaczego nie było to możliwe we wcześniejszych standardach C ++?
Jak wskazano w komentarzach, clang nie akceptuje tego kodu przedstawionego w wersji demo, co sugeruje, że może to być rzeczywiście błąd w gcc.
I złożył raport o błędzie na Bugzilla GCC
"c string" == Foo<std::string>("foo")
)).Odpowiedzi:
GCC ma błąd.
Wyszukiwanie nazw jest zawsze wykonywane dla nazw szablonów pojawiających się przed
<
, nawet jeśli nazwa ta jest nazwą deklarowaną w deklaracji (przyjaciel, wyraźna specjalizacja lub wyraźna instancja).Ponieważ nazwa
operator==
w deklaracji znajomego jest nazwą niekwalifikowaną i podlega wyszukiwaniu nazw w szablonie, obowiązują zasady dwufazowego wyszukiwania nazw. W tym kontekścieoperator==
nie jest nazwą zależną (nie jest częścią wywołania funkcji, więc ADL nie ma zastosowania), więc nazwa jest sprawdzana i związana w punkcie, w którym się pojawia (patrz [temp. Brak] akapit 1). Twój przykład jest źle sformułowany, ponieważ to wyszukiwanie nazw nie znajduje deklaracjioperator==
.Spodziewałbym się, że GCC akceptuje to w trybie C ++ 20 z powodu P0846R0 , który pozwala (na przykład)
operator==<T>(a, b)
na użycie w szablonie, nawet jeśli wcześniejsza deklaracjaoperator==
jako szablonu nie jest widoczna.Oto jeszcze bardziej interesujący przypadek testowy:
Z
-DWRONG_DECL
, GCC i Clang zgadzają się, że ten program jest źle sformułowany: niewykwalifikowane wyszukiwanie deklaracji znajomego nr 2 w kontekście definicji szablonu znajduje deklarację nr 1, która nie pasuje do instancji przyjacielaFoo<int>
. Deklaracja nr 3 nie jest nawet brana pod uwagę, ponieważ niewykwalifikowane wyszukiwanie w szablonie jej nie znajduje.Z
-UWRONG_DECL
, GCC (w C ++ 17 i wcześniejszych) i Clang zgadzają się, że ten program jest źle sformułowany z innego powodu: niewykwalifikowane wyszukiwanieoperator==
w linii nr 2 nie znajduje niczego.Ale z
-UWRONG_DECL
, GCC w trybie C ++ 20 wydaje się decydować, że jest OK, że niewykwalifikowane wyszukiwanieoperator==
w # 2 kończy się niepowodzeniem (prawdopodobnie z powodu P0846R0), a następnie wydaje się ponawiać wyszukiwanie z kontekstu tworzenia szablonów, znajdując teraz # 3, w naruszenie normalnej dwufazowej reguły wyszukiwania nazw dla szablonów.źródło