sınıfım için nasıl takas işlevi sağlayabilirim?


89

swapSTL algoritmalarımı etkinleştirmenin doğru yolu nedir ?

1) Üye swap. Mu std::swapüyesini kullanmak SFINAE hile kullanmak swap.

2) swapAynı isim alanında serbest duruş .

3) Kısmi uzmanlaşma std::swap.

4) Yukarıdakilerin tümü.

Teşekkür ederim.

DÜZENLEME: Görünüşe göre sorumu açıkça ifade etmedim. Temel olarak, bir şablon sınıfım var ve o sınıf için yazdığım (verimli) takas yöntemini kullanmak için STL algoritmalarına ihtiyacım var.

Yanıtlar:


95
  1. Doğru olan kullanım içinde swap. "Kitaplık" kodunu yazarken ve ADL'yi (argümana bağlı arama) etkinleştirmek istediğinizde bu şekilde yazın swap. Ayrıca, bunun SFINAE ile ilgisi yoktur.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs) {
    using std::swap; // enable 'std::swap' to be found
                    // if no other 'swap' is found through ADL
    // some code ...
    swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                    // or falls back on 'std::swap'
    // more code ...
}
  1. swapSınıfınız için bir işlev sağlamanın doğru yolu .
namespace Foo {

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs) {
    // ...
}

}

Eğer swap1'de gösterildiği gibi şimdi) kullanılır, böylece fonksiyon bulunacaktır. Ayrıca, kesinlikle ihtiyacınız varsa bu işlevi bir arkadaş yapabilir veya swapücretsiz işlev tarafından çağrılan bir üye sağlayabilirsiniz :

// version 1
class Bar{
public:
    friend void swap(Bar& lhs, Bar& rhs) {
    // ....
    }
};

// version 2
class Bar{
public:
    void swap(Bar& other) {
    // ...
    }
};

void swap(Bar& lhs, Bar& rhs) {
    lhs.swap(rhs);
}

...
  1. Açık bir uzmanlık demek istiyorsun. Kısmi hala başka bir şeydir ve işlevler için de mümkün değildir, sadece yapılar / sınıflar. Eğer uzman olamaz çünkü Gibi, std::swapşablon sınıflar için, size sahip senin ad alanında serbest fonksiyonu sağlamak. Fena bir şey değil, eğer söyleyebilirsem. Şimdi, açık bir uzmanlaşma da mümkündür, ancak genellikle bir işlev şablonunda uzmanlaşmak istemezsiniz :
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. Hayır, 1) 2) ve 3) 'ten farklıdır. Ayrıca, hem 2) hem de 3) 'e sahip olmak her zaman 2)' nin seçilmesine yol açacaktır çünkü daha iyi uyuyor.

8
Bir şeyi yanlış okumadığım sürece, sizin (1) ve sorunun (1) gerçekten aynı hizada değil. Yine de +1
Dennis Zickefoose

1
@Xeo. Girdiğiniz için teşekkürler. Sorumu düzenledim. STL, 1. durumda tanımladığınız gibi takas kullanıyor mu?
pic11

1
@pic: Evet, STL, 1) 'de gösterdiğim ADL takasını kullanacak, ancak yalnızca bir üye işlevi değil, ücretsiz bir işlev varsa. Bkz. 2) ve 3), her iki sürüm de algoritmalar tarafından seçilecektir. Tavsiye ederim 2), çünkü 3) tarihi geçmiş ve kötü uygulama olarak kabul ediliyor.
Xeo

2
İlk kod parçasındaki yorum yanıltıcıdır. using std::swap;ADL'yi etkinleştirmez, sadece derleyicinin std::swapADL uygun bir aşırı yükleme bulup bulamadığını tespit etmesine izin verir .
David Rodríguez - dribeas

6
Bu cevap teknik olarak doğru, ama fena halde netlik için düzenleme ihtiyacı. OP'nin (1) doğru cevap olmadığı, çünkü bu cevabın aşırı hızlı okunması yanlışlıkla işaret ediyor gibi görünmektedir.
Howard Hinnant

1

Sınıfların şablon sınıflar olabileceği EDIT'e cevap vermek için uzmanlığa ihtiyacınız yok. şöyle bir sınıf düşünün:

template <class T>
struct vec3
{
    T x,y,z;
};

aşağıdaki gibi sınıfları tanımlayabilirsiniz:

vec3<float> a;
vec3<double> b;
vec3<int> c;

3 takasın tümünü uygulamak için bir işlev yaratabilmek istiyorsanız (bu örnek sınıfın bunu garanti ettiği anlamına gelmez) tıpkı Xeo'nun (2) 'de söylediği gibi ... uzmanlaşma olmadan, ancak sadece normal bir şablon işlevi yaparsınız:

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);
}

Takas şablonu işlevi, takas etmeye çalıştığınız sınıfla aynı ad alanında bulunmalıdır. ADL kullanarak söz konusu ad alanına başvurmasanız bile aşağıdaki yöntem bu takası bulacak ve kullanacaktır:

using std::swap;
swap(a,b);

0

Görünüşe göre (2) ( kullanıcı tanımlı sınıfın bildirildiği aynı ad alanında serbest durmaswap ) , kullanıcı tanımlı bir sınıf sağlamak swapiçin izin verilen tek yol , çünkü ad alanına bildirim eklemek stdgenellikle tanımlanmamış bir davranış. Std ad alanını genişletme (cppreference.com) :

Aşağıda belirtilen birkaç istisna dışında, ad alanına stdveya içinde yer alan herhangi bir ad alanına bildirim veya tanım eklemek tanımsız bir davranıştır.std

Ve swapbu istisnalardan biri olarak gösterilmez. Dolayısıyla ad alanına kendi swapaşırı yüklemenizi eklemek stdtanımsız bir davranıştır.

Ayrıca, standart kitaplığın, eğer böyle bir kullanıcı tanımlı sağlanmışsa bir kullanıcı sınıfı için kullanıcı swaptanımlı çağrı yapmak için işleve niteliksiz bir çağrı kullandığı da söylenir .swapswap

Değiştirilebilir (cppreference.com) :

Birçok standart kütüphane işlevi (örneğin, birçok algoritma) argümanlarının Swappable'ı tatmin etmesini bekler , yani standart kütüphane bir takas yaptığında, eşdeğerini kullanır using std::swap; swap(t, u);.

takas (www.cplusplus.com) :

Standart kitaplığın (iç std) birçok bileşeni, temel olmayan türler için özel aşırı yüklemelerin bu genel sürüm yerine çağrılmasına izin vermek swapiçin niteliksiz bir şekilde çağırır : Sağlandıkları türleswap aynı ad alanında bildirilen özel aşırı yükler seçilir bu genel sürüm üzerinden bağımsız değişkene bağlı arama yoluyla .

Ancak, std::swapkullanıcı tanımlı bir sınıf için işlevi doğrudan kullanmanın, kullanıcı tanımlı std::swapyerine genel sürümü çağırdığını unutmayın swap:

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

Bu nedenle, swapkullanıcı kodundaki işlevi, standart kitaplıkta olduğu gibi çağırmanız önerilir :

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.