2013-02-16 14 views
10

Geçerli durumda, C++ 11 (diyelim ki gcc 4.7.2), değişken argümanları alabilecek bir kurucuya ihtiyacım olduğunda variadic-template veya std::initializer_list arasında nasıl seçim yapmalıyım?Kurucular için, variadic-templates vs std :: initializer_list arasında nasıl seçim yapabilirim?

+3

Emin değilim (bu bir yorumdur neden), ancak başlatıcı listesinin tümü aynı türde olmak zorundayken, variadic şablonlar farklı türleri işlemez mi? –

+0

@JoachimPileborg, Kesinlikle doğru, daha sonra int ... 've' std :: initializer_list 'arasında seçim yapabilirsiniz. Kullanmak için daha doğal olanı seçelim diyorum. – chris

cevap

15

Variadic bir şablon, farklı türlerde bağımsız değişkenler sağlarken, std::initializer_list argümanın türü ile templated edilir. Bu, listedeki tüm öğelerin türünün aynı olması gerektiği (veya temeldeki türe dönüştürülebilmesi, ancak daraltma dönüşümlerine izin verilmemesi) anlamına gelir. Bunun sizin için tercih edilip edilmeyeceğine bağlı olarak, birini veya diğerini seçebilirsiniz.

Ayrıca variadic şablon benzer tip kesinti initializer_list için gerçekleştirilemez yaparken, sözdizimsel formu T&& lvalue referanslar ve rvalue referanslar hem bağlanabildiği içinde, mükemmel yönlendirme gerekiyorsa genellikle varsayılan seçimdir:

struct A 
{ 
    // Deduces T& for lvalue references, T for rvalue references, and binds to both 
    template<typename... Ts> 
    A(Ts&&...) { } 

    // This is an rvalue reference to an initializer_list. The above type deduction 
    // does not apply here 
    template<typename T> 
    A(initializer_list<T>&&) { } 
}; 

Ayrıca tek tip başlatma sözdizimi (yani küme parantezleri) kullandığınızda bir initializer_list kabul eden bir yapıcı uygulanabilir, yeni bir kurucu var olsa bile, varsayılan olarak çağrılan olacağını fark.

struct A 
{ 
    A(int i) { } 
}; 

struct B 
{ 
    B(int) { } 
    B(std::initializer_list<A>) { } 
}; 

int main() 
{ 
    B b {1}; // Will invoke the constructor accepting initializer_list 
} 
+3

Mükemmel iletme hakkında biraz genişletme, 'std :: initializer_list :: reference' (örn.' * L.begin() 'sonuç türü),' T const & ', hamleleri engeller. Yalnızca hareket değerleri yerleştirebilirsin ama onları dışarı çıkaramazsın. –

+0

@LucDanton: İyi nokta. –

3

Ben variadic şablonları seçilmesi hep tavsiye ve mümkün std::initializer_list kaçının: Bu ya sahip olmak istediklerine şey olabilir veya olmayabilir.

Bu

Birlikte std::vector hayata nasıl olurdu C++ 11:

nedeni olarak oldu parantez türüne göre farklı davranışlara yol açabilir jenerik kodunda initializer_list kullanarak, main gösterdi edilir
#include <iostream> 
#include <vector> 

struct exp_sequence { 
    template <typename... T> 
    exp_sequence(T&&...) {} 
}; 

struct from_arglist_t {} from_arglist; 

template <typename T> 
class my_vector { 
    std::vector<T> data; 

public: 
    my_vector(int n, T const& e) : data(n, e) {} 

    template <typename... Args> 
    my_vector(from_arglist_t, Args&&... args) { 
    data.reserve(sizeof...(Args)); 
    exp_sequence{(data.push_back(std::forward<Args>(args)),1)...}; 
    } 

    std::size_t size() { return data.size(); } 
}; 

int main() 
{ 
    std::vector<int> v1{13, 13}; std::cout << v1.size() << '\n'; // 2 
    std::vector<int> v2(13, 13); std::cout << v2.size() << '\n'; // 13 

    my_vector<int> v3{13, 13}; std::cout << v3.size() << '\n'; // 13 
    my_vector<int> v4(13, 13); std::cout << v4.size() << '\n'; // 13 
    my_vector<int> v5(from_arglist, 13, 13); std::cout << v5.size() << '\n'; // 2 
    my_vector<int> v6{from_arglist, 13, 13}; std::cout << v6.size() << '\n'; // 2 
} 

seçilen. Böyle bir kurucu ekleyerek kodu sessizce değiştirme olasılığı da vardır. variadic şablonla

//std::vector<move_only> m1{move_only{}}; // won't compile 
my_vector<move_only> m2{from_arglist, move_only{}}; // works fine 
+0

'exp_sequence' hilesi düzgün, ancak öğeleri doğru sıraya yerleştirmeyi garanti ediyor mu? AFAIR, C ve C++, işlev argümanlarını sözcüksel olarak değerlendirmeyi taahhüt etmemişlerdir. Bu nedenle, varyantik şablon yapımcıları için argüman değerlendirme sırası hakkında C++ 11 standardında özel bir garanti yoksa, bu taşınabilir değildir. – fgp

+1

@fgp: Argüman değerlendirme sırasının soldan sağa doğru sıralanması garanti edilir.Bu, brace initialisation'ın her kullanımında geçerlidir (bu nedenle exp_sequence'ın bir sınıf olması gerekir). @ipc – ipc

+1

'{... (1 öne (args) :: data.push_back (std))}' exp_sequence oluyor tam olarak ne? Neden '(' ve ', 1)' ile data.push_back (std :: forward (args)) 'i çevrelemeye ihtiyacımız var? Ve neden 1 '? – 0xbadf00d

4

, argüman sayısı (sizeof... yoluyla ve erişilebilir) derleme sırasında bilinmektedir:

başka neden

hareket okunur türleridir. std::initializer_list ile argüman sayısı sadece çalışma zamanında bilinir. Bu yüzden kararın bir kısmı, kaç argümanınızın bulunduğunu bilmek veya bilmek istediğiniz zamana bağlıdır.

+0

kapsayıcı, tam durumda başlatılması gerektiğinden (push_back, vb.) Derleme zamanı boyutuna sahiptir. Eğer 'const (boyut) 'metodunun' constexpr' olarak işaretlenmediğini kastediyorsanız, bu C++ 11'de bir kusurdu ve C++ 14'te düzeltildi. Böylece, adlandırılmış bir 'initializer_list ', vb. boyutlarında şablonlar, birçok şekilde derleme zamanında kullanılabilir. ama elbette, belki de variadic seçenek olarak pek fazla yol değil! Bu _parameter_ –

+2

Bir yapıcı olduğunu bir 'kurucu her yerinde farklı büyüklükteki başlatıcıları ile birden sitelerden çağrılabilir çünkü initializer_list', derleme sırasında bilinen bir büyüklüğe sahip değildir. – KnowItAllWannabe

+0

Evet, harika bir nokta, daha önce tam düşünmüyordum :) –

İlgili konular