- jest właściwe korzystanie z
swap
. Napisz to w ten sposób, kiedy piszesz kod „biblioteki” i chcesz włączyć ADL (wyszukiwanie zależne od argumentów) swap
. Nie ma to też nic wspólnego z SFINAE.
template<class T>
void foo(T& lhs, T& rhs) {
using std::swap;
swap(lhs, rhs);
}
- To właściwy sposób na zapewnienie
swap
funkcji dla Twojej klasy.
namespace Foo {
class Bar{};
void swap(Bar& lhs, Bar& rhs) {
}
}
Jeśli swap
jest teraz używany, jak pokazano w 1), Twoja funkcja zostanie znaleziona. Możesz również zaprzyjaźnić się z tą funkcją, jeśli jest to absolutnie konieczne, lub zapewnić element członkowski swap
wywoływany przez funkcję bezpłatną:
class Bar{
public:
friend void swap(Bar& lhs, Bar& rhs) {
}
};
class Bar{
public:
void swap(Bar& other) {
}
};
void swap(Bar& lhs, Bar& rhs) {
lhs.swap(rhs);
}
...
- Masz na myśli wyraźną specjalizację. Częściowe jest nadal czymś innym i również nie jest możliwe w przypadku funkcji, tylko struktury / klasy. Jako taki, ponieważ nie można specjalizować
std::swap
dla klas szablon, mają zapewnić bezpłatną funkcję w swojej przestrzeni nazw. Nieźle, jeśli mogę tak powiedzieć. Teraz możliwa jest również jawna specjalizacja, ale generalnie nie chcesz specjalizować się w szablonie funkcji :
namespace std
{
template<>
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
}
}
- Nie, ponieważ 1) różni się od 2) i 3). Ponadto posiadanie zarówno 2), jak i 3) spowoduje, że zawsze będziesz mieć 2) wybrane, ponieważ lepiej pasuje.
using std::swap;
nie włącza ADL, po prostu pozwala kompilatorowi zlokalizować,std::swap
jeśli ADL nie znajdzie odpowiedniego przeciążenia.Aby odpowiedzieć na EDYCJĘ, gdzie klasy mogą być klasami szablonowymi, w ogóle nie potrzebujesz specjalizacji. rozważ taką klasę:
template <class T> struct vec3 { T x,y,z; };
możesz zdefiniować takie klasy jak:
vec3<float> a; vec3<double> b; vec3<int> c;
jeśli chcesz mieć możliwość stworzenia jednej funkcji do zaimplementowania wszystkich 3 zamian (nie żeby ta przykładowa klasa to uzasadniała), robisz tak, jak powiedział Xeo w (2) ... bez specjalizacji, ale po prostu utwórz zwykłą funkcję szablonu:
template <class T> void swap(vec3<T> &a, vec3<T> &b) { using std::swap; swap(a.x,b.x); swap(a.y,b.y); swap(a.z,b.z); }
Funkcja szablonu wymiany powinna znajdować się w tej samej przestrzeni nazw, co klasa, którą próbujesz zamienić. następująca metoda znajdzie i użyje tej zamiany, nawet jeśli nie odwołujesz się do tej przestrzeni nazw za pomocą ADL:
using std::swap; swap(a,b);
źródło
Wydaje się, że (2) ( wolnostojący
swap
w tej samej przestrzeni nazw, w której zadeklarowana jest klasa zdefiniowana przez użytkownika ) jest jedynym dozwolonym sposobem zapewnieniaswap
klasy zdefiniowanej przez użytkownika, ponieważ dodawanie deklaracji do przestrzeni nazwstd
jest zwykle niezdefiniowanym zachowaniem. Rozszerzenie przestrzeni nazw std (cppreference.com) :I
swap
nie jest oznaczony jako jeden z tych wyjątków. Zatem dodanie własnegoswap
przeciążenia dostd
przestrzeni nazw jest niezdefiniowanym zachowaniem.Mówi się również, że biblioteka standardowa używa niekwalifikowanego wywołania
swap
funkcji w celu wywołania zdefiniowanejswap
przez użytkownika klasy użytkownika, jeśli taka zdefiniowana przez użytkownikaswap
jest dostarczona.Możliwość wymiany (cppreference.com) :
swap (www.cplusplus.com) :
Zwróć jednak uwagę, że bezpośrednie użycie
std::swap
funkcji dla klasy zdefiniowanej przez użytkownika wywołuje ogólną wersjęstd::swap
zamiast zdefiniowanej przez użytkownikaswap
:my::object a, b; std::swap(a, b); // calls std::swap, not my::swap
Dlatego zalecane jest wywołanie
swap
funkcji w kodzie użytkownika w taki sam sposób, jak to się robi w standardowej bibliotece:my::object a, b; using std::swap; swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
źródło