Ответ 1
Короткий ответ: "Это не очень хорошо".
Он вызывает f
для каждого из args...
и отбрасывает возвращаемое значение. Но он делает это так, что приводит к неожиданному поведению в ряде случаев, бесполезно.
У кода нет гарантий заказа, и если возвращаемое значение f
для данного Arg
имеет перегруженный operator,
, он может иметь неприятные побочные эффекты.
С небольшим пробелом:
[](...){}(
(
f(std::forward<Args>(args)), 0
)...
);
Мы начнем изнутри.
f(std::forward<Args>(args))
- это неполное утверждение, которое можно разложить с помощью a ...
. Он будет вызывать f
на одном из args
при расширении. Вызовите это утверждение INVOKE_F
.
(INVOKE_F, 0)
принимает возвращаемое значение f(args)
, применяет operator,
, затем 0
. Если возвращаемое значение не имеет переопределений, это отбрасывает возвращаемое значение f(args)
и возвращает a 0
. Назовите это INVOKE_F_0
. Если f
возвращает тип с переопределением operator,(int)
, здесь происходят плохие вещи, и если этот оператор возвращает не-POD-esque-тип, вы можете получить "условно поддерживаемое" поведение позже.
[](...){}
создает лямбда, которая в качестве единственного аргумента принимает вариацию C-стиля. Это не то же самое, что пакеты параметров С++ 11 или С++ 14 variardic lambdas. Возможно, незаконно передавать типы не-POD-esque в функцию ...
. Назовите это HELPER
HELPER(INVOKE_F_0...)
- это расширение пакета параметров. в контексте вызова HELPER
operator()
, что является юридическим контекстом. Оценка аргументов не определена, и из-за подписи HELPER
INVOKE_F_0...
вероятно, должны содержаться только простые старые данные (в языке С++ 03), или, более конкретно, [expr.call]/p7 говорит: (через @ТС)
Передача потенциально оцененного аргумента типа класса (раздел 9), имеющего нетривиальный конструктор копирования, нетривиальный конструктор перемещения или нетривиальный деструктор без соответствующего параметра, условно поддерживается с помощью семантики, определяемой реализацией,
Таким образом, проблемы этого кода заключаются в том, что порядок не задан, и он полагается на хорошо выполненные типы или конкретные варианты реализации компилятора.
Мы можем исправить проблему operator,
следующим образом:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((void(f(std::forward<Args>(args))), 0)...);
}
то мы можем гарантировать порядок путем расширения в инициализаторе:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {(void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
но выше не выполняется, когда args...
пуст, поэтому добавьте еще один 0
:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {0, (void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
и нет никаких оснований для того, чтобы компилятор НЕ удалял unused[]
из existance, но по-прежнему оценивал f
на args...
в порядке.
Мой предпочтительный вариант:
template <class...F>
void do_in_order(F&&... f) {
int unused[] = {0, (void(std::forward<F>(f)()), 0)...};
void(unused); // suppresses warnings
}
который принимает нулевую лямбда и запускает их по одному, слева направо. (Если компилятор может доказать, что порядок не имеет значения, он может свободно вывести их из строя).
Затем мы можем реализовать выше:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
do_in_order( [&]{ f(std::forward<Args>(args)); }... );
}
который помещает "странное расширение" в изолированную функцию (do_in_order
), и мы можем использовать ее в другом месте. Мы также можем написать do_in_any_order
, который работает аналогичным образом, но делает сообщение any_order
понятным: однако, запрещая экстремальные причины, код, выполняемый в предсказуемом порядке в расширении пакета параметров, уменьшает неожиданность и сохраняет головные боли до минимума.
Недостатком метода do_in_order
является то, что не все компиляторы, подобные ему, - расширение пакета параметров, содержащего инструкцию, содержащую целые подзапросы, не является тем, что они ожидают сделать.