2

я наткнулся на этот пост variadic template function to concatenate std::vector containers предполагая использование следующего синтаксиса:int a [] {(functioncall (a1, a2), 0) ...}; (Недействительный (а)); Что означает этот синтаксис?

template<typename T> 
void append_to_vector(std::vector<T>& v1, const std::vector<T>& v2) { 
    std::cout << v2[0] << std::endl; 
    for (auto& e : v2) v1.push_back(e); 
} 


template<typename T, typename... A> 
std::vector<T> concat_version3(std::vector<T> v1, const A&... vr) { 
    int unpack[] { (append_to_vector(v1, vr), 1)... }; 
    (void(unpack)); 
    return v1; 
} 

Я начал играть с ним, чтобы понять, как это работает, так как я не видел этого:

int unpack[] { (append_to_vector(v1, vr), 0)... }; 
(void(unpack)); 

Похоже, это какой-то динамически сгенерированный список инициализации, который также имеет побочные эффекты? Меня также озадачивает тот факт, что 0 выше не имеет значения. Я заменил -1 и 5, и каждый из этих значений работал очень хорошо.

Так может кто-то сказать мне название этой техники/синтаксиса и что именно происходит в двух строках выше? Я бы очень признателен за любые указания и извиниться, если я пропустил соответствующие сообщения SO.

+1

Этот метод позволяет использовать функцию 'append_to_vector (v1, X)' один раз для каждого из приведенных вариационных аргументов, при этом каждый элемент заменяется на 'X'. Я называю это «фиктивным массивом», не знаю, имеет ли оно официальное название :) –

ответ

4
int unpack[] { (append_to_vector(v1, vr), 1)... }; 
//  ^^ |       | ||| | array of ints 
//   ^      | |^ array initializer 
//         ^ |  comma operator 
//           ^^^  pack expansion 

Это создает массив с int, содержащих столько элементов, как размер параметра пакета vr. Каждый элемент массива равен 1, что и возвращает comma operator после оценки обоих аргументов. Конечный эллипс указывает pack expansion пакета параметров vr.

Так что, если бы вы назвать вашу функцию concat_version3(v1, v2, v3), где все аргументы являются vector s, то приведенное выше выражение приведет к

int unpack[]{ (append_to_vector(v1, v2), 1), (append_to_vector(v1, v3), 1) }; 

Хорошая вещь об оценке выражений в рамно-Init-лист заключается в том, что порядок оценки фиксирован и происходит слева направо.

§8.5.4/4 [dcl.init.list]

В инициализаторе-листе из рамно-инициализации-листа, в инициализаторе-положении, в то числе любые, которые являются результатом разложения пакетов (14.5.3), равны в порядке их появления.

Так вы гарантированно, что v2 добавляемый к v1 перед тем v3, что то, что вы хотите.


(void(unpack)); 

Это просто способ избежать неиспользуемых переменных предупреждений от компилятора.


Теперь я хотел бы написать unpack инициализацию немного по-другому.

int unpack[] { 1, (append_to_vector(v1, vr), 1)... }; 
//    ^^ 

В оригинале, если вы назвали функцию concat_version3(v1), т.е. с пустым параметром пакета, код не будет компилироваться, так как вы будете пытаться создать массив нулевого размера, добавление дополнительных исправлений элементов эта проблема.

Кроме того, если вы использовали вышеуказанное выражение в более общем коде, где вы не знали, что такое тип возврата append_to_vector, тогда вам также необходимо защититься от возможности возврата типа, который перегружает запятой. В этом случае вы бы написать

int unpack[] { 1, (append_to_vector(v1, vr), void(), 1)... }; 

Добавив выражение void() между вы убедитесь, что не перегруженный оператор запятая не выбран, а встроенный в одном всегда называется.


Наконец, если у вас есть компилятор, который понимает fold expressions, вы можете избавиться от всего трюка массива и просто написать

template<typename T, typename... A> 
std::vector<T> concat_version3(std::vector<T> v1, const A&... vr) 
{ 
    (void)(((append_to_vector(v1, vr), void()), ...)); 
    return v1; 
} 

Live demo

Примечание: дополнительные скобки после void из-за clang bug.

+0

Большое спасибо за этот очень тщательный ответ. Весьма признателен. – sunny

+0

Случай сложения выражения может также использовать листинг 'void' (для защиты от перегруженных запятых). –

+0

@ T.C. Спасибо, не считали эту возможность, так или иначе складывающиеся выражения казались слишком классными, чтобы быть восприимчивыми к таким ловушкам :) Исправлено. – Praetorian