#c #c 17 #c 14 #variadic-templates
Вопрос:
Я пытаюсь понять приведенный ниже код. Скопировано непосредственно с видео Джейсона Тернера на YouTube
#include <iostream>
#include <sstream>
#include <vector>
template<typename ...T>
std::vector<std::string> print(const Tamp; ...t)
{
std::vector<std::string> retval;
std::stringstream ss;
(void)std::initializer_list<int>{
(
ss.str(""),
ss << t,
retval.push_back(ss.str()),
0)...
};
return retval;
}
int main()
{
for( const auto amp;s : print("Hello", "World", 5.4, 1.1, 2.2) ) {
std::cout << s << "n";
}
}
Вопросы :
- Может ли кто-нибудь предоставить расширенное представление кода в initializer_list? Мне трудно представить, как расширяется утверждение в каждом аргументе? Делает ли ss.str(«»), ss < Я не могу представить, как будет выглядеть расширенный список инициализаторов?
- Зачем нам нужен фиктивный «0» в конце списка инициализаторов? Что произойдет, если у меня этого не будет?
- Как я могу легко просмотреть … расширение в коде, которым я поделился?
Комментарии:
1. (1) —
( )...
означает, что все в( )
повторяется для каждого аргумента. (2) —0
не «в конце», оно повторяется для каждого аргумента. Поскольку это ainitializer_list<int>
,0
в данном случае каждый элемент должен быть целым числом.2. Это не похоже на код C 17. В C 17 будут использоваться выражения сгиба.
3. Если вы хотите увидеть, как шаблоны могут расширяться, вы можете использовать C Insights .
4. @HolyBlackCat : Что вы подразумеваете под этим утверждением в своем первоначальном ответе «Поскольку это initializer_list<int>, каждый элемент должен быть целым числом, в данном случае 0». Чего мы достигаем, добавляя ноль? Что произойдет, если я не добавлю этот ноль? Не могли бы вы привести более простой и независимый пример?
5. @Проверьте это только там, чтобы весь результат выражения был типом
int
(push_back
возвращаемымvoid
)
Ответ №1:
T
и t
являются пакетами параметров.
Существует два основных способа использования пакета: выражение сгиба (в C 17 и новее) и просто обычное расширение пакета.
Выражение сгиба будет выглядеть следующим образом:
((ss.str(""), ss << t, retval.push_back(ss.str())), ...);
Выражение Fold повторяет свой операнд для каждого элемента пакета, вставляя некоторый оператор ( ,
в данном случае) между частями, принадлежащими каждому аргументу. Тот, что выше, расширяется до:
((ss.str(""), ss << t1, retval.push_back(ss.str())), // <-- Inserted commas
(ss.str(""), ss << t2, retval.push_back(ss.str())), // <--
(ss.str(""), ss << t3, retval.push_back(ss.str())));
Регулярное расширение аналогично, за исключением того, что оно всегда генерирует запятую, и эта запятая не может быть оператором (в отличие, например, от разделителя между инициализаторами массива или аргументами функции).
Например, если бы вы писали (ss.str(""), ss << t, retval.push_back(ss.str()))...;
в предположении, что это будет работать так, как это выражение сгиба, оно не сработало бы, потому что результирующая запятая должна была бы быть оператором.
Из-за этого ограничения до C 17 люди использовали фиктивные массивы (или initializer_list
s, как в вашем примере). Вот как это будет выглядеть с массивом:
int dummy[] = {(ss.str(""), ss << t, retval.push_back(ss.str()), 0)...};
Это распространяется на:
int dummy[] = {(ss.str(""), ss << t1, retval.push_back(ss.str()), 0),
(ss.str(""), ss << t2, retval.push_back(ss.str()), 0),
(ss.str(""), ss << t3, retval.push_back(ss.str()), 0)};
Здесь размер массива (или initializer_list
) соответствует размеру пакета.
,0
это необходимо , потому что каждый элемент массива является an int
, поэтому он должен быть инициализирован с помощью an int
.
Комментарии:
1. @HolyBlackCat — нужно ли вообще объявлять фиктивный массив int? Разве я не могу просто использовать список инициализаторов и устранить необходимость в 0 ? Разве нет чего-то, называемого пустым списком инициализаторов, например {} ?
2. @Test Я не совсем понимаю вашу идею, но нет, вам нужен либо массив, либо
initializer_list
(оба с элементами фиктивного типа, напримерint
).3. @HolyBlackCat — Мое единственное сомнение здесь в том, зачем мне нужен фиктивный массив ИЛИ список фиктивных инициализаторов? Какое правило c регулирует это требование?
4. @Test Я попытался объяснить это в ответе, начиная с «Регулярное расширение похоже …».