Mały przegląd. Piszę szablon klasy, który zapewnia silny typedef; silnym typedef kontrastuję ze zwykłym typedef, który właśnie deklaruje alias. Aby dać pomysł:Dlaczego operatory porównania std :: vector i std :: string są zdefiniowane jako funkcje szablonu?
using EmployeeId = StrongTypedef<int>;
Teraz istnieją różne szkoły myślenia na temat silnych typedefs i niejawnych konwersji. Jedna z tych szkół mówi: nie każda liczba całkowita jest EmployeeId, ale każdy EmployeeId jest liczbą całkowitą, więc powinieneś zezwolić na niejawne konwersje z EmployeeId na liczbę całkowitą. I można zaimplementować to i pisać takie rzeczy jak:
EmployeeId x(4);
assert(x == 4);
To działa, ponieważ x
dostaje niejawnie konwertowane na liczbę całkowitą, a następnie porównanie całkowitą równość jest używany. Jak na razie dobrze. Teraz chcę zrobić to z wektorem liczb całkowitych:
using EmployeeScores = StrongTypedef<std::vector<int>>;
Więc mogę robić takie rzeczy:
std::vector<int> v1{1,2};
EmployeeScores e(v1);
std::vector<int> v2(e); // implicit conversion
assert(v1 == v2);
Ale nadal nie mogę tego zrobić:
assert(v1 == e);
Powód, dla którego to nie działa, wynika z tego, jak std::vector
definiuje kontrolę równości, zasadniczo (modulo standardese):
template <class T, class A>
bool operator==(const vector<T,A> & v1, const vector<T,A> & v2) {
...
}
To jest szablon funkcji; ponieważ zostanie odrzucony we wcześniejszej fazie wyszukiwania, nie pozwoli na porównanie typu, który konwertuje się niejawnie na wektor.
Innym sposobem definiowania równości byłoby tak:
template <class T, class A = std::allocator<T>>
class vector {
... // body
friend bool operator==(const vector & v1, const vector & v2) {
...
}
} // end of class vector
W tym drugim przypadku, operator równości nie jest szablonem funkcji, to po prostu zwykła funkcja, która jest generowana wraz z klasą, podobny do funkcja członka. Jest to nietypowy przypadek włączony przez słowo kluczowe przyjaciel.
Pytanie (przepraszam, że tło było tak długie), dlaczego zamiast numeru std::vector
użyć drugiego formularza zamiast pierwszego? To sprawia, że vector
zachowuje się bardziej jak prymitywne typy, a jak widać wyraźnie pomaga w moim przypadku użycia. To zachowanie jest jeszcze bardziej zaskakujące w przypadku string
, ponieważ łatwo zapomnieć, że string
jest po prostu typedef szablonu klasy.
Dwie rzeczy, które rozważyłem: po pierwsze, niektórzy mogą sądzić, że ponieważ funkcja przyjaciół zostanie wygenerowana z klasą, spowoduje to ciężką awarię, jeśli zawarty typ wektora nie obsługuje porównania równości. Nie o to chodzi; podobnie jak funkcje składowe klas szablonów, nie są generowane, jeśli nie są używane. Po drugie, w bardziej ogólnym przypadku wolne funkcje mają tę zaletę, że nie muszą być zdefiniowane w tym samym nagłówku, co klasa, co może mieć zalety. Ale to wyraźnie nie jest tutaj wykorzystywane.
Co daje? Czy istnieje ku temu dobry powód, czy był to po prostu nieoptymalny wybór?
Edytuj: Napisałem krótki przykład demonstrujący dwie rzeczy: zarówno ta niejawna konwersja działa zgodnie z życzeniem z podejściem przyjacielskim, jak i to, że nie występują żadne poważne awarie, jeśli szablonowy typ nie spełnia wymagań operatora równości (oczywiście zakładając, że operator równości nie jest używany w tym przypadku).Edytuj: poprawiono, aby przeciwstawić się pierwszemu podejściu: http://coliru.stacked-crooked.com/a/6f8910945f4ed346.
dlaczego to zostało odrzucone? – Untitled123
Czy jesteś pewien, że z 'friend'version,' assert (v1 == e); 'skompilowałoby? – YSC
@YSC Tak, dodałem link do Coliru, daj mi znać, jeśli w pełni odpowiada on na twoje pytanie. –