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 swap
iş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 swap
bunlardan 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::swap
iş 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 friend
iş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::swap
ve 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 friend
işlevi? Bu alanda karışıklık var.
C ++ standartlaştırılmadan önce, friend
fonksiyonlar "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 friend
iş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 std
ve 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::swap
izledi : 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::swap
sadece için tamamlanmamış bir arama yapar swap
ile std::swap
ilişkili ad olarak. Bu, şeylerin tekrar özlü olmasına yardımcı olur, ancak yine de bir serseri.
std::swap
Ben 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 friend
işlev tamamlanmıştır ve çalışır. Ve takas yaptığınızda ya kullanın boost::swap
ya swap
da std::swap
iliş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::swap
normalde dikkate alınmaz; ancak ilişkilendirebiliriz (niteliksiz olarak kabul edilen aşırı yükler kümesine ekleyebiliriz swap
) ve bulunmasına izin verebiliriz.