Каков самый простой способ обработать значения пакета параметров шаблона в правильном порядке без использования свернутых выражений (C++11)

Я хотел бы перенести следующий код на C++11:

template<unsigned i>
static void bar() { /* some code with compile-time optimizations for each value i */ }

template <unsigned... I>
void f()
{
  ((bar<I>()),...);
}

Порядок вызова 'bar' для каждого значения пакета параметров I важен - который, как я считаю, работает в реализации C++17 выше, потому что выражение свёртки использует оператор запятой.

Изначально я выбрал явно рекурсивную реализацию:

template <unsigned... I>
typename std::enable_if<sizeof...(I) == 0>::type g() {}

template <unsigned head, unsigned... I>
void g()
{
  bar<head>();
  g<I...>();
}

Кажется, что это работает, но требует двух реализаций для g(). Пытаясь перейти к одной функции, я прочитал, что расширение пакета произойдет в make_tuple, и подумал, что это сработает:

template<unsigned... I>
static void h1()
{
    std::make_tuple( (bar<I>(), 0)... );
}

к сожалению, это не дает никаких гарантий в отношении порядка выполнения - на самом деле с gcc порядок выполнения в точности обратный. В качестве альтернативы я мог бы использовать список инициализации в фигурных скобках:

template<unsigned... I>
static void h2()
{
  using expand = int[];
  expand{ 0, ( bar<I>(), 0) ... };
}

Кажется, что это сохраняет порядок с gcc, но я не мог понять, является ли это просто совпадением.

Итак, конкретные вопросы:

  • будет ли реализация h2 гарантировать правильный порядок исполнения?
  • есть ли альтернативные реализации, которые я пропустил?

1 ответ

Решение

Вы правы, что ваш h1не гарантирует порядок оценки в любой версии C++, так как инициализация параметров функции имеет неопределенную последовательность. Вашh2гарантирует порядок оценки в C++11 и новее. Фактически, любой метод, который помещает вызовы как элементы{ список в скобках } гарантирует это.

Все версии C++, начиная с C++11, содержат параграф [dcl.init.list] / 4:

В инициализаторах-лист о наличии рамно-INIT-листе, то инициализатор-пункт с, в том числе любого такого результата из пакета расширений ([temp.variadic]), вычисляется в том порядке, в котором они появляются. То есть каждое вычисление значения и побочный эффект, связанный с данным предложением инициализатора, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным с любым предложением инициализатора, которое следует за ним в списке разделенных запятыми списка инициализаторов. [Примечание: этот порядок оценки сохраняется независимо от семантики инициализации; например, это применяется, когда элементы списка инициализаторов интерпретируются как аргументы вызова конструктора, даже если обычно нет ограничений последовательности для аргументов вызова. - конец примечания]

Другие вопросы по тегам