Yazmanın birkaç yolu vardır swap, bazıları diğerlerinden daha iyidir. Zamanla, tek bir tanımın en iyi sonucu verdiği bulundu. Bir swapişlev yazma hakkında nasıl düşünebileceğimizi düşünelim .
Öncelikle aşağıdaki gibi kapların std::vector<>tek bağımsız değişken üye işlevine sahip olduğunu görüyoruz swap:
struct vector
{
void swap(vector&) { /* swap members */ }
};
Doğal olarak, sınıfımız da öyle olmalı, değil mi? Pek değil. Standart kütüphane her türlü gereksiz şeye sahiptir ve üye swapbunlardan biridir. Neden? Hadi devam edelim.
Yapmamız gereken kanonik ne tanımlamak ve bizim sınıf neyi ihtiyacı onunla çalışmak için yapmak. Ve kanonik takas yöntemi ile std::swap. Bu nedenle üye işlevleri yararlı değildir: genel olarak bir şeyleri nasıl değiştirmemiz gerektiği ve davranışları üzerinde hiçbir etkisi olmaması gerekir std::swap.
Öyleyse, std::swapiş yapmak std::vector<>için uzmanlaşma sağlamalıyız (ve sağlamalıydık) std::swap, değil mi?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
Bu kesinlikle bu durumda işe yarar, ancak göze batan bir sorunu var: fonksiyon uzmanlıkları kısmi olamaz. Yani, şablon sınıflarını bununla, sadece belirli örneklerle uzmanlaştıramayız:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
Bu yöntem çoğu zaman çalışır, ancak her zaman işe yaramaz. Daha iyi bir yol olmalı.
Var! Bir friendişlevi kullanabilir ve ADL aracılığıyla bulabiliriz :
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
Bir şeyi değiştirmek istediğimizde, † ilişkilendiririz std::swapve ardından niteliksiz bir çağrı yaparız:
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
Bir nedir friendişlevi? Bu alanda karışıklık var.
C ++ standartlaştırılmadan önce, friendfonksiyonlar "arkadaş adı enjeksiyonu" olarak adlandırılan bir şey yaptı, burada kod sanki fonksiyonun etrafındaki isim alanında yazılmış gibi davranıyordu . Örneğin, bunlar eşdeğer ön standarttı:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
Ancak, ADL icat edildiğinde bu kaldırıldı. Bu durumda friendişlev yalnızca ADL aracılığıyla bulunabilir; Ücretsiz bir fonksiyonu olarak isterse, buna (böylece ilan edilmesi için gerekli görüyoruz örneğin). Ama lo! Bir problem vardı.
Sadece kullanırsanız std::swap(x, y), aşırı yükünüz asla bulunmayacaktır, çünkü açıkça "içeri bakın stdve başka hiçbir yerde" demiştiniz ! Bu yüzden bazı insanlar iki işlev yazmayı önerdi: biri ADL aracılığıyla bulunacak bir işlev olarak , diğeri ise açık std::nitelikleri ele almak için .
Ama gördüğümüz gibi, bu her durumda işe yaramaz ve sonunda çirkin bir karmaşa ile sonuçlanır. Bunun yerine, deyimsel takas diğer rotayı std::swapizledi : sınıfın sağladığı işi yapmak yerine swap, yukarıdaki gibi nitelikli kullanmadığından emin olmak, swapçıların işi . Ve insanlar bunu bildiği sürece, bu oldukça iyi çalışma eğilimindedir. Ama sorun burada yatıyor: kalifiye olmayan bir çağrı kullanmanız istenmiyor!
Bunu kolaylaştırmak için, Boost gibi bazı kütüphaneler işlevini temin boost::swapsadece için tamamlanmamış bir arama yapar swapile std::swapilişkili ad olarak. Bu, şeylerin tekrar özlü olmasına yardımcı olur, ancak yine de bir serseri.
std::swapBen ve diğerleri yanlışlıkla böyle olacağını düşündüm davranışında C ++ 11 değişiklik olmadığını unutmayın . Eğer bunu bitirdiyseniz, burada okuyun .
Kısacası: üye işlevi sadece gürültüdür, uzmanlık çirkin ve eksiktir, ancak friendişlev tamamlanmıştır ve çalışır. Ve takas yaptığınızda ya kullanın boost::swapya swapda std::swapilişkili olmayan bir niteliksiz .
† Gayri resmi olarak, bir işlev çağrısı sırasında dikkate alınacak bir ad ilişkilendirilir . Ayrıntılar için §3.4.2'yi okuyun. Bu durumda, std::swapnormalde dikkate alınmaz; ancak ilişkilendirebiliriz (niteliksiz olarak kabul edilen aşırı yükler kümesine ekleyebiliriz swap) ve bulunmasına izin verebiliriz.