Списки инициализаторов С++ 11 по параметрам вариационного шаблона: почему это не работает
Включение параметров вариационного шаблона в списки инициализаторов должно гарантировать, что они оцениваются по порядку, но здесь не происходит:
#include <iostream>
using namespace std;
template<class T> void some_function(T var)
{
cout << var << endl;
}
struct expand_aux {
template<typename... Args> expand_aux(Args&&...) { }
};
template<typename... Args> inline void expand(Args&&... args)
{
bool b[] = {(some_function(std::forward<Args>(args)),true)...}; // This output is 42, "true", false and is correct
cout << "other output" << endl;
expand_aux temp3 { (some_function(std::forward<Args>(args)),true)... }; // This output isn't correct, it is false, "true", 42
}
int main()
{
expand(42, "true", false);
return 0;
}
Как получилось?
Ответы
Ответ 1
Кажется, это ошибка . Выход должен быть тем, который вы ожидаете.
Пока нет гарантии по порядку оценки аргументов вызова конструктора вообще, есть гарантия на порядок оценки выражений в скобках списка инициализаторов.
В соответствии с пунктом 8.5.4/4 стандарта С++ 11:
В списке инициализаторов списка бит-init-инициализатор-предложения, включая все, что получается из пакета разложения (14.5.3), оцениваются в том порядке, в котором они появляются. То есть вычисление всех значений и побочный эффект, связанный с заданным предложением инициализатора, секвенирован перед каждым вычислением значения и стороной эффект, связанный с любым предложением инициализатора, которое следует за ним в разделенном запятыми списке списка инициализаторов. [Примечание. Это упорядочение оценки выполняется независимо от семантики инициализации; например, применяется когда элементы списка инициализатора интерпретируются как аргументы вызова конструктора, хотя обычно нет аргументов последовательности аргументов вызова. -end note]
Ответ 2
Как уже отмечалось, ваша проблема - ошибка компилятора. Ваш код, как написано, должен оценивать его аргументы в порядке.
Мой совет будет заключаться в том, чтобы быть явным в отношении того, что вы хотите делать, и в каком порядке вы это делаете, и избегать злоупотребления оператором запятой (так как в стороне ваш код мог бы вести себя странно, если some_function
возвратил тип, который переопределяет operator,
) или использует списки инициализационных списков (которые, хотя и стандартные, также являются относительно неясными).
Мое решение - написать, а затем использовать do_in_order
:
// do nothing in order means do nothing:
void do_in_order() {}
// do the first passed in nullary object, then the rest, in order:
template<typename F0, typename... Fs>
void do_in_order(F0&& f0, Fs&&... fs) {
std::forward<F0>(f0)();
do_in_order( std::forward<Fs>(fs)... );
}
который вы используете следующим образом:
do_in_order( [&]{ some_function(std::forward<Args>(args)); }... );
вы завершаете действие, которое вы хотите сделать в анонимной лубрибельной полной ламбе, а затем используйте ...
для создания всего набора экземпляров указанных лямбда и перейдите к do_in_order
, который вызывает их по порядку с помощью совершенной пересылки.
Для компилятора должно быть легко встроить и сократить последовательность вызовов. И он говорит о том, что он делает непосредственно, и не требует странных пустых бросков, использования оператора запятой или массивов, значение которых отбрасывается.