Последовательность среди вариативного расширения

Для этого невариативного примера:

int     Func1();
double  Func2();
void    MyFunc( int, double );

int  main()
{
    MyFunc( Func1(), Func2() );
    //...
}

не указано, вычисляется ли сначала Func1() или Func2(), просто оба должны быть выполнены до вызова MyFunc().

Как эта последовательность работает с расширением аргументов с переменным числом аргументов?

template < typename Func, typename ...Args >
void  MyFunc2( Func &&f, Args&& ...a )
{
    int  b[] = { f( std::forward<Args>(a) )... };
    //...
}

Допустим, f — это функциональный объект, который меняет свое состояние после первого вызова. Будет ли f вызываться по порядку для каждого сегмента a? Другими словами, будет ли f вызываться для первого элемента в списке a, затем для второго элемента, третьего и т. д., вместо случайного пропуска расширенного списка? Есть ли то, что мы использовали для обозначения точек последовательности между каждым элементом?


person CTMacUser    schedule 26.05.2012    source источник
comment
Что означает то, что мы привыкли называть точками последовательности?   -  person Johannes Schaub - litb    schedule 26.05.2012
comment
Я немного знаю об этом, но такие утверждения, как наличие точки последовательности между x и y, были заменены на x упорядочены до < я>у. Новое полное определение является более точным и включает приспособления для многопоточности.   -  person CTMacUser    schedule 27.05.2012


Ответы (1)


Да, списки инициализаторов, заключенные в фигурные скобки, гарантируют порядок оценки слева направо, а вызовы функций — нет. Таким образом, MyFunc2 будет упорядочен правильно.

Об этом говорится в статье Википедии: https://en.wikipedia.org/wiki/Variadic_templates.

Есть ли то, что мы использовали для обозначения точек последовательности между каждым элементом?

Нет, хотя он использует токен запятой, это не оператор запятой.

person Pubby    schedule 26.05.2012
comment
Интересный. Однако пример с pass не работает с GCC-4.7. Выражения в pass{std::cout << args...} на моей машине выполняются справа налево. Это ошибка в GCC-4.7? Или описание в Википедии неверное? - person nosid; 26.05.2012
comment
@nosid Кажется, я помню, что слышал, что в GCC была эта ошибка. litb написал этот фрагмент статьи, и он никогда не ошибается (хе-хе, я спрошу его в следующий раз, когда он появится в сети) - person Pubby; 26.05.2012
comment
Ты прав. Я нашел это в §8.5.4.4 (черновик n3290): [..] Предложения инициализатора [..] оцениваются в том порядке, в котором они появляются. и Этот порядок оценки сохраняется [..] - даже несмотря на то, что обычно нет ограничений последовательности для аргументов вызова. - person nosid; 26.05.2012
comment
Я уже знал, что разделители не являются оператором-запятой из другого моего вопроса, на который ответил @nosid. - person CTMacUser; 27.05.2012
comment
Хотя во втором примере для инициализации использовалось вариативное расширение, я хотел знать, выполняется ли расширение последовательно при любых обстоятельствах. Оглядываясь вокруг, ответ здесь правильный, что это не так. Раздел о вариативных шаблонах (14.5.3) не дает указаний по порядку и указывает, что расширение действует точно так же, как обычный список, разделенный запятыми, в том же месте. Таким образом, правила последовательности соответствуют тому, что было бы в обычном списке. (Списки инициализации в фигурных скобках упорядочены в 8.5.4/p4, аргументы функций не упорядочены в 8.3.6/p9.) - person CTMacUser; 27.05.2012