Variadic şablonlar bağlamında “…” simgesi için kurallar nelerdir?


99

C ++ 11'de bunun gibi çeşitli şablonlar vardır:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args )
{
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

Bazı Meraklısına bu konuda vardır: ifadesi std::forward<Args>(args)...hem kullanır Argsve argsancak sadece bir ...belirteç. Ayrıca std::forward, yalnızca bir şablon parametresi ve bir bağımsız değişken alan, değişken olmayan bir şablon işlevidir. Bunun sözdizimi kuralları nelerdir (kabaca)? Nasıl genelleştirilebilir?

Ayrıca: İşlev uygulamasında, ellipsis ( ...) ilgi ifadesinin sonundadır. Şablon bağımsız değişken listesinde ve parametre listesinde üç noktasının ortada olmasının bir nedeni var mı?


2
Kısaca ikinci kısımda: Bir şablon parametre paketi veya fonksiyon parametre paketi "bildirilirken" ..., tanıtıcı tanıtılmadan önce gelir. Paket türlerinden birini veya her ikisini kullanırken, ...genişletmek için ifade modelinden sonra gelir.
aschepler

Yanıtlar:


99

Varyadik şablon bağlamında, üç nokta ..., bir ifadenin sağ tarafında görünüyorsa ( bir an için bu ifade desenini çağırın) şablon parametre paketini açmak için kullanılır veya bu, paketin sol tarafında görünüyorsa bir paket argümanıdır . isim:

...thing  // pack   : appears as template arguments
thing...  // unpack : appears when consuming the arguments

Kural, sol tarafındaki desen ne olursa olsun ...tekrarlanır - paketlenmemiş desenler ( şimdi ifadeler olarak adlandırılır ) virgülle ayrılır ,.

En iyi bazı örneklerle anlaşılabilir. Şu işlev şablonuna sahip olduğunuzu varsayalım:

template<typename ...T> //pack
void f(T ... args)      //pack
{
   // here are unpack patterns

   g( args... );        //pattern = args
   h( x(args)... );     //pattern = x(args)
   m( y(args...) );     //pattern = args (as argument to y())
   n( z<T>(args)... );  //pattern = z<T>(args)
}

Şimdi, bu işlevi geçiş Tolarak {int, char, short}çağırırsam, işlev çağrılarının her biri şu şekilde genişletilir:

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

Gönderdiğiniz kodda, işlev çağrısı ile std::forwardgösterilen dördüncü modeli izler n().

Yukarıdaki x(args)...ve arasındaki farka dikkat y(args...)edin!


...Bir diziyi başlatmak için şu şekilde de kullanabilirsiniz :

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

buna genişletilen:

std::vector<data_info> v 
{ 
   {arg0, sizeof(int)},
   {arg1, sizeof(char)},
   {arg2, sizeof(short)}
};

publicAşağıdaki örnekte gösterildiği gibi bir modelin erişim belirtecini bile içerebileceğini yeni fark ettim :

template<typename ... Mixins>
struct mixture : public Mixins ...  //pattern = public Mixins
{
    //code
};

Bu örnekte, desen şu şekilde genişletilmiştir:

struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN  

Yani, tüm temel sınıflardan halka açık olarak mixturetüretilir .

Umarım yardımcı olur.


1
Eşleşen ifade ile ilgili olarak; Mümkün olan en büyük ifade olmalı, değil mi? Örneğin x+args...genişletilmeli x+arg0,x+arg1,x+arg2, değil x+arg0,arg1,arg2.
bitmask

Bu nedenle, ...modeldeki her genişletilebilir varlık için geçerlidir.
Orbit'te Hafiflik Yarışları

@bitmask: Evet. x+args...genişletmek gerektiğini x+arg0,x+arg1,x+arg2, değil x+arg0,arg1,arg2 .
Nawaz

1
@ Jarod42: C ++ 17 yayınlandığında bu yanıtı güncelleyeceğim.
Nawaz

3
@synther: sizeof...(T)orada gerekli değildir. Basitçe yazabilirsiniz:int a[] = { ___ };
Nawaz

48

Aşağıdakiler, GoingNative 2012'de Andrei Alexandrescu'nun "Variadic Templates are Funadic" konuşmasından alınmıştır. Bunu çeşitli şablonlar hakkında iyi bir giriş için tavsiye edebilirim.


Değişken bir paketle yapılabilecek iki şey vardır. sizeof...(vs)Eleman sayısını elde etmek ve genişletmek için başvurmak mümkündür .

Genişletme kuralları

Use            Expansion

Ts...          T1, ..., Tn
Ts&&...        T1&&, ..., Tn&&
x<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

Genişleme içe doğru ilerler. Kilit adımında iki listeyi genişletirken, aynı boyuta sahip olmaları gerekir.

Daha fazla örnek:

gun(A<Ts...>::hun(vs)...);

Tüm genişletir Tsşablon argümanı listesinde Ave sonra işlev huntüm genişletilmiş olur vs.

gun(A<Ts...>::hun(vs...));

TsŞablon bağımsız değişken listesindeki Atümünü ve tümünü vsişlev bağımsız değişkenleri olarak genişletir hun.

gun(A<Ts>::hun(vs)...);

Fonksiyonu hunile Tsve vskilit adımında genişletir .

Not:

Tsbir tür değildir ve vsbir değer değildir! Bir tür / değer listesi için takma adlardır. Her iki liste de potansiyel olarak boş olabilir. Her ikisi de yalnızca belirli eylemlere uyar. Dolayısıyla aşağıdakiler mümkün değildir:

typedef Ts MyList;  // error!
Ts var;             // error!
auto copy = vs;     // error!

Genişleme lokusu

Fonksiyon argümanları

template <typename... Ts>
void fun(Ts... vs)

Başlatıcı listeleri

any a[] = { vs... };

Temel belirleyiciler

template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<Ts>... { /**/ };

Üye başlatıcı listeleri

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

Geçici bağımsız değişken listeleri

std::map<Ts...> m;

Yalnızca argümanlar için olası bir eşleşme varsa derlenir.

Listeleri yakala

template <class... Ts> void fun(Ts... vs) {
    auto g = [&vs...] { return gun(vs...); }
    g();
}

Öznitelik listeleri

struct [[ Ts... ]] IAmFromTheFuture {};

Şartnamede yer almaktadır, ancak henüz bir tür olarak ifade edilebilecek bir nitelik yoktur.


Güzel. Fonksiyon argümanları lokusların dışında bırakılır, ancak: P
Potatoswatter

@Potatoswatter Teşekkürler. Fonksiyon bağımsız değişken listesindeki genişletmeden bahsedilmedi.
typ1232

@ typ1232 Düzenleme için özür dilerim, ancak orijinal gönderinizin intihale çok yakın olduğunu hissettim. Btw, ben de bir süre önce o konuşmayı izledim - harika.
Walter
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.