Какой самый простой способ обработать значения пакета параметров шаблона в правильном порядке без использования выражений сворачивания (C ++ 11) - PullRequest
0 голосов
/ 12 марта 2020

Я хотел бы перенести следующий код на 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' для каждого значения пакета параметров, который я важен, - который я считаю работает в реализации 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)... );
}

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

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

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

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

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

1 Ответ

1 голос
/ 13 марта 2020

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

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

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

...