Короткий ответ: «Это не очень хорошо».
Он вызывает f
для каждого из args...
и отбрасывает возвращаемое значение. Но это делает так, что в ряде случаев приводит к неожиданному поведению.
Код не имеет гарантий порядка, и если возвращаемое значение f
для данного Arg
имеет перегруженный operator,
может иметь неприятные побочные эффекты.
С некоторым белым пространством:
[](...){}(
(
f(std::forward<Args>(args)), 0
)...
);
Мы начнем изнутри.
f(std::forward<Args>(args))
- это неполное утверждение, которое можно разложить на ...
. Он будет вызывать f
на одном из args
при расширении. Вызовите это утверждение INVOKE_F
.
(INVOKE_F, 0)
принимает возвращаемое значение f(args)
, применяет operator,
, затем 0
. Если возвращаемое значение не имеет переопределений, это отбрасывает возвращаемое значение f(args)
и возвращает значение 0
. Назовите это INVOKE_F_0
. Если f
возвращает тип с переопределением operator,(int)
, здесь происходят плохие вещи, и если этот оператор возвращает не-POD-esque-тип, вы можете получить «условно поддерживаемое» поведение позже.
[](...){}
создает лямбда, которая принимает вариации C-стиля как единственный аргумент. Это не то же самое, что пакеты параметров C ++ 11, или C ++ 14 variadic lambdas. Возможно, незаконно передавать не-POD-esque типы в функцию ...
. Вызвать это HELPER
HELPER(INVOKE_F_0...)
- это расширение пакета параметров. в контексте вызова HELPER
's operator()
, который является юридическим контекстом. Оценка аргументов не определена, и из-за подписи HELPER
INVOKE_F_0...
, вероятно, должны содержаться только простые старые данные (в языке C ++ 03), или, более конкретно, [expr.call] / p7 говорит: (через @ TC)
Передача потенциально оцененного аргумента типа класса (раздел 9), имеющего нетривиальный конструктор копирования, нетривиальный конструктор перемещения или нетривиальный деструктор без соответствующего параметра, условно поддерживается семантикой, определяемой реализацией.
blockquote>Таким образом, проблемы этого кода заключаются в том, что порядок не задан и , он полагается на варианты выбора подходящего варианта компилятора или .
Мы можем исправить проблему
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
является то, что не все компиляторы, подобные ему, - расширение пакета параметров, содержащего инструкцию, содержащую целые подзапросы, не является тем, что они ожидают сделать.
Просто петля ...
foreach(var table in DataSet1.Tables) {
foreach(var col in table.Columns) {
...
}
foreach(var row in table.Rows) {
object[] values = row.ItemArray;
...
}
}
foreach (DataTable table in dataSet.Tables)
{
foreach (DataRow row in table.Rows)
{
foreach (object item in row.ItemArray)
{
// read item
}
}
}
Или, если вам нужна информация о столбце:
foreach (DataTable table in dataSet.Tables)
{
foreach (DataRow row in table.Rows)
{
foreach (DataColumn column in table.Columns)
{
object item = row[column];
// read column and item
}
}
}