Які правила для токена “…” у контексті різних шаблонів?


98

У C ++ 11 є різні шаблони на зразок цього:

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

У цьому є деякі цікавості: Вираз std::forward<Args>(args)...використовує як Argsі argsлише один ...маркер. Крім того std::forward, це неваріадична функція шаблону, яка бере лише один параметр шаблону та один аргумент. Які правила синтаксису для цього (приблизно)? Як це можна узагальнити?

Також: У реалізації функції еліпсис ( ...) знаходиться в кінці вираження інтересу. Чи є причина, що в списку аргументів шаблону та списку параметрів еліпсис знаходиться посередині?


2
Коротко про другу частину: Коли "декларується" пакет параметрів шаблону або пакет функціональних параметрів, ...перед початком введення ідентифікатора йдеться. Якщо ви використовуєте один або обидва типи пачок, ...після виразного шаблону з'являється розширення.
aschepler

Відповіді:


99

У контексті варіативного шаблону еліпсис ...використовується для розпакування пакета параметрів шаблону, якщо він з’являється в правій частині виразу (на момент зателефонуйте до цього шаблону виразів ). Правило полягає в тому, що будь-який візерунок з лівої сторони ...повторюється - розпаковані візерунки (називаємо їх виразами зараз) відокремлюються комою ,.

Це можна найкраще зрозуміти на деяких прикладах. Припустимо, у вас є такий шаблон функції:

template<typename ...T>
void f(T ... args) 
{
   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)
}

Тепер, якщо я називаю цю функцію передачею Tяк {int, char, short}, то кожен виклик функції розгортається як:

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

У опублікованому вами коді std::forwardслідує четвертий візерунок, проілюстрований n()функцією виклику.

Зверніть увагу на різницю між x(args)...і y(args...)вище!


Ви можете використовувати ...ініціалізацію масиву також як:

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

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

який розширюється на це:

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

Я щойно зрозумів, що модель може включати навіть специфікатор доступу, як public, як показано в наступному прикладі:

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

У цьому прикладі шаблон розширюється як:

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

Тобто mixtureвиходить публічно з усіх базових класів.

Сподіваюся, що це допомагає.


1
Стосовно виразу, який відповідає; Це має бути найбільшим можливим виразом, чи не так? Наприклад, x+args...слід розширити x+arg0,x+arg1,x+arg2, а не x+arg0,arg1,arg2.
бітмаска

Тож ...стосується кожного розширюваного об'єкта в шаблоні.
Гонки легкості по орбіті

@bitmask: Так. x+args...повинен розширюватися x+arg0,x+arg1,x+arg2, НЕ x+arg0,arg1,arg2 .
Наваз

1
@ Jarod42: Цю відповідь я оновлю, як тільки вийде C ++ 17.
Наваз

3
@synther: sizeof...(T)там не потрібно. Ви можете просто написати:int a[] = { ___ };
Nawaz

48

Далі взято з бесіди "Варіадичні шаблони є Funadic" Андрія Олександреску в GoingNative 2012. Я можу порекомендувати її для гарного вступу про різні шаблони.


Є дві речі, які можна зробити з різноманітною упаковкою. Можна застосувати, sizeof...(vs)щоб отримати кількість елементів і розширити його.

Правила розширення

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)

Розширення протікає всередину. Під час розширення двох списків у режимі блокування вони повинні мати однаковий розмір.

Більше прикладів:

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

Розгортає все Tsу списку аргументів шаблону, Aа потім функція hunрозширюється з усіма vs.

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

Розгортає все Tsу списку аргументів шаблону Aі все vsяк аргументи функції для hun.

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

Розширює функцію за hunдопомогою Tsта vsв режимі блокування.

Примітка:

Tsне є типом і vsне є значенням! Вони є псевдонімами для списку типів / значень. Будь-який список може бути потенційно порожнім. Обидва підкоряються лише конкретним діям. Тому неможливо таке:

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

Локуси розширення

Аргументи функції

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

Списки ініціалізаторів

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

Базові специфікатори

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

Списки ініціалізаторів учасників

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

Списки аргументів шаблону

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

Компілюється лише за наявності можливої ​​відповідності аргументів.

Списки захоплення

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

Списки атрибутів

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

Він є в специфікації, але ще немає атрибута, який би можна було виразити як тип.


Приємно. Однак аргументи функції залишаються поза локусами: P
Potatoswatter

@Potatoswatter Дякую Розширення у списку аргументів функцій не згадувалося.
type1232

@ typ1232 Вибачте за редагування, але я відчув, що ваш початковий пост був занадто близький до плагіату. До речі, я також спостерігав, що розмова якийсь час тому - приголомшливий.
Вальтер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.