Вызов функтора для элементов переменного списка параметров
От: rg45 СССР  
Дата: 30.03.17 19:56
Оценка:
Время от времени возникает потребность вывзвать какой-то функтор для каждого элемента из переменного списка параметров.

template <typename F, typename...Args>
void ForEach(F&& f, Args&&...args)
{
   //???
}


При этом важно сохранить порядок следования элементов. До сих пор не знаю, как лучше это реализовать, каждый раз приходится прибегать к какому-нибудь извращению типа такого:

http://ideone.com/lVDSHX
template <typename F, typename...Args>
void ForEach(F&& f, Args&&...args)
{
   auto wrapper = [&](auto&& t){ f(std::forward<decltype(t)>(t)); return 0; };
   std::initializer_list<int>{ wrapper(std::forward<Args>(args))... };
}
}


Заранее благодарен тому, кто облегчит мои страдания.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Вызов функтора для элементов переменного списка параметр
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 20:03
Оценка: 35 (2)
Folding expressions?

насколько я понял, должно получиться что-то типа:
template<typename F, typename... Args>
void for_all(F &&f, Args&& ...args) {
   (std::forward<F>(f)(std::forward<Args>(args)), ...);
}
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 30.03.2017 20:05 niXman . Предыдущая версия .
Re[2]: Вызов функтора для элементов переменного списка параметр
От: rg45 СССР  
Дата: 30.03.17 20:10
Оценка:
Здравствуйте, niXman, Вы писали:

X>насколько я понял, должно получиться что-то типа:

X>
X>template<typename F, typename... Args>
X>void for_all(F &&f, Args&& ...args) {
X>   (std::forward<F>(f)(std::forward<Args>(args)), ...);
X>}
X>


Спасибо!
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Вызов функтора для элементов переменного списка параметр
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 20:10
Оценка: +1
Здравствуйте, niXman, Вы писали:

X>Folding expressions?

ну да, работает.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: Вызов функтора для элементов переменного списка параметр
От: rg45 СССР  
Дата: 30.03.17 20:17
Оценка:
Здравствуйте, niXman, Вы писали:

X>Здравствуйте, niXman, Вы писали:


X>>Folding expressions?

X>ну да, работает.

Я был уверен, что при распаковке запятая добавится автоматически и пытался писать так:
(std::forward<F>(f)(std::forward<Args>(args))...);

Ну в самом деле, когда мы очень похожим образом конструируем тот же initializer_list, у нас же нет необходимости самим писать эту запятую? Вот я этот момент не очень догоняю.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Вызов функтора для элементов переменного списка параметр
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 20:21
Оценка: 18 (1)
Здравствуйте, rg45, Вы писали:

R>Я был уверен, что при распаковке запятая добавится автоматически и пытался писать так:

R>
R>(std::forward<F>(f)(std::forward<Args>(args))...);
R>

R>Ну в самом деле, когда мы очень похожим образом конструируем тот же initializer_list, у нас же нет необходимости самим писать эту запятую? Вот я этот момент не очень догоняю.

ну... Folding expressions это не просто перечисление, а несколько большее, потому и синтаксис другой.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[5]: Вызов функтора для элементов переменного списка параметр
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 20:31
Оценка: 2 (1) +1
Здравствуйте, niXman, Вы писали:

X>ну... Folding expressions это не просто перечисление, а несколько большее, потому и синтаксис другой.


замечательная фитча! можно и такое сделать:
#include <iostream>

template<typename F, typename ...Args>
bool for_all(F &&f, Args&& ...args) {
   return (std::forward<F>(f)(std::forward<Args>(args)) && ...);
}

int main()
{
    auto f = [](auto &&v) { std::cout << v << std::endl; return true; };
    return for_all(f, 1, 3.14, '4', "5");
}

тыц.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[6]: Вызов функтора для элементов переменного списка парам
От: rg45 СССР  
Дата: 30.03.17 20:35
Оценка:
Здравствуйте, niXman, Вы писали:

X>замечательная фитча! можно и такое сделать:

X>...

http://ideone.com/k6zsGx
template <typename...Args>
std::ostream& PrintAll(std::ostream& output, Args&&...args)
{
    return (output << ... << args) << std::endl;
}


Класс!

Недаром меня не покидало ощущение, что я фигней занимаюсь
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 30.03.2017 20:37 rg45 . Предыдущая версия .
Re[2]: Вызов функтора для элементов переменного списка параметр
От: watchmaker  
Дата: 30.03.17 20:44
Оценка: +1
Здравствуйте, niXman, Вы писали:

X>  std::forward<F>(f)

Не самый очевидный способ оставить в коде бомбу замедленного действия :)
Кто-нибудь передаст в качестве f функцию, у которой будет перегрузка по rvalue ref-qualifier (вроде метода mf3 тут), и программа поведёт себя неожиданным образом.

Конечно же в правильном коде никакой std::forward вокруг f не нужен. Да и вообще непонятно зачем тут к f применять forward было — на производительность он всё равно почти никогда не повлияет, а когда повлияет, то как раз за счёт нарушения корректности программы.
Re[3]: Вызов функтора для элементов переменного списка параметр
От: rg45 СССР  
Дата: 30.03.17 20:51
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, niXman, Вы писали:


W>
X>>  std::forward<F>(f)
W>

W>Не самый очевидный способ оставить в коде бомбу замедленного действия
W>Кто-нибудь передаст в качестве f функцию, у которой будет перегрузка по rvalue ref-qualifier (вроде метода mf3 тут), и программа поведёт себя неожиданным образом.

W>Конечно же в правильном коде никакой std::forward вокруг f не нужен. Да и вообще непонятно зачем тут к f применять forward было — на производительность он всё равно почти никогда не повлияет, а когда повлияет, то как раз за счёт нарушения корректности программы.


ИМХО, как раз в случае подобном mf3 forward как раз и нужен — зачем-то же эта перегрузка существует? Ну а в остальных случах forward не повредит, коль скоро ни на что не влияет. Ну кроме дополнительной громоздкости кода разве что.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Вызов функтора для элементов переменного списка парам
От: watchmaker  
Дата: 30.03.17 20:58
Оценка:
Здравствуйте, rg45, Вы писали:

R> зачем-то же эта перегрузка существует?

Она существует для того, чтобы передать информацию о том, что мы вызвали operator() от объекта, который можно курочить как угодно потому что это последний вызов, условно говоря.
Что неправда в вышеприведённом коде.

R>ИМХО, как раз в случае подобном mf3 forward как раз и нужен

Вот более простой пример:
vector<int> v0, v1, v2, v3;
// fill v0

v1 = v0;
v2 = v0;
v3 = v0;

тут содержимое вектора v0 копируется в вектора v1,v2,v3.
А вот в этом коде происходит чёрт знает что:
v1 = std::move(v0);
v2 = std::move(v0);
v3 = std::move(v0);

При этом каждая строчка в отдельности полностью корректная. Но в сумме получается совсем не копирование v0 в v1,v2,v3.

С вызовом operator() всё то же самое. Каждый вызов в отдельности правилен. А вот серия вызовов — уже не всегда.

Спасает этот код лишь то, что rvalue ref-qualifier в программах нечасто используется.
Отредактировано 30.03.2017 20:59 watchmaker . Предыдущая версия .
Re[3]: Вызов функтора для элементов переменного списка параметр
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 21:15
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>
X>>  std::forward<F>(f)
W>

W>Не самый очевидный способ оставить в коде бомбу замедленного действия

да такое сплошь и рядом делается, я просто взял за привычку делать так же.
пример:
namespace detail {

template <class F, class... Args>
auto INVOKE(F&& f, Args&&... args)
    noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
 -> std::enable_if_t<!std::is_member_pointer_v<std::decay_t<F>>,
    decltype(std::forward<F>(f)(std::forward<Args>(args)...))>
{
      return std::forward<F>(f)(std::forward<Args>(args)...);
}

} // namespace detail
 
template< class F, class... ArgTypes >
auto invoke(F&& f, ArgTypes&&... args)
    // exception specification for QoI
    noexcept(noexcept(detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...)))
 -> decltype(detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...))
{
    return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: Вызов функтора для элементов переменного списка парам
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 21:21
Оценка:
а вот пример из реальной реализации. неужели они не понимают про "бомбу замедленного действия"?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 30.03.2017 21:22 niXman . Предыдущая версия .
Re[4]: Вызов функтора для элементов переменного списка парам
От: watchmaker  
Дата: 30.03.17 21:26
Оценка: 64 (4) +1
Здравствуйте, niXman, Вы писали:



X>да такое сплошь и рядом делается, я просто взял за привычку делать так же.

X>пример
Эти примеры используют forward правильно. А твой код нет. Вот и всё отличие :)

Ключевой момент в том, что ни в одном из этих примеров не происходит повторного обращения к f после единственного вызова.

Смотри, если f был временным объектом. То std::forward<F>(f) будет работать точно так же как и std::move(f) — собственно для этого std::forward и придумали.
Но тогда вызов for_all(create_object(), 1, 2, 3) развернётся в
std::move(f)(1);
std::move(f)(2);
std::move(f)(3);

Не понятно ещё где тут проблема? Тогда смотри в соседнюю ветку на пример с векторами: http://rsdn.org/forum/cpp/6742199.1
Автор: watchmaker
Дата: 30.03.17


Правильный код был бы таким:
         (f)(1);
         (f)(2);
         (f)(3);

Или, если уж упарываться по оптимизации, то таким:
         (f)(1);
         (f)(2);
std::move(f)(3);


То есть std::forward безопасно звать только для последнего параметра.
Отредактировано 30.03.2017 21:27 watchmaker . Предыдущая версия .
Re[5]: Вызов функтора для элементов переменного списка парам
От: niXman Ниоткуда https://github.com/niXman
Дата: 30.03.17 21:30
Оценка: +1
Здравствуйте, watchmaker, Вы писали:

W>Эти примеры используют forward правильно. А твой код нет. Вот и всё отличие

W>Ключевой момент в том, что ни в одном из этих примеров не происходит повторного обращения к f после единственного вызова.

я понял. упустил эту ключевую разницу
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: Вызов функтора для элементов переменного списка параметр
От: andyp  
Дата: 30.03.17 21:50
Оценка: 12 (3)
Здравствуйте, rg45, Вы писали:



R>При этом важно сохранить порядок следования элементов. До сих пор не знаю, как лучше это реализовать, каждый раз приходится прибегать к какому-нибудь извращению типа такого:


  было
R>http://ideone.com/lVDSHX
R>
R>template <typename F, typename...Args>
R>void ForEach(F&& f, Args&&...args)
R>{
R>   auto wrapper = [&](auto&& t){ f(std::forward<decltype(t)>(t)); return 0; };
R>   std::initializer_list<int>{ wrapper(std::forward<Args>(args))... };
R>}
R>}
R>


R>Заранее благодарен тому, кто облегчит мои страдания.


если с++17 под рукой нет и враппера не хочется, то вот по С++14:

#include <iostream>
#include <utility>

template <typename F, typename...Args>
void ForEach(F&& f, Args&&...args)
{
   //auto wrapper = [&](auto&& t){ f(std::forward<decltype(t)>(t)); return 0; };
   auto v = {(f(args),0) ... };
   //тут написать что-то , чтобы компилятор не ругался на неиспользуемую переменную
   (void) v;
}

int main()
{
   ForEach([](auto&& t){ std::cout << t; }, "Pi = ", 3.14);
}


http://ideone.com/jhekqM
Отредактировано 30.03.2017 21:52 andyp . Предыдущая версия .
Re[5]: Вызов функтора для элементов переменного списка парам
От: uzhas Ниоткуда  
Дата: 31.03.17 10:02
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Смотри, если f был временным объектом. То std::forward<F>(f) будет работать точно так же как и std::move(f) — собственно для этого std::forward и придумали.

W>Но тогда вызов for_all(create_object(), 1, 2, 3) развернётся в
W>
W>std::move(f)(1);
W>std::move(f)(2);
W>std::move(f)(3);
W>

W>Не понятно ещё где тут проблема? Тогда смотри в соседнюю ветку на пример с векторами: http://rsdn.org/forum/cpp/6742199.1
Автор: watchmaker
Дата: 30.03.17


что-то я не вижу тут проблем, можно разжевать?
разве выражение std::move(f)(1) не эквивалентно f(1) ?
Re[6]: Вызов функтора для элементов переменного списка парам
От: rg45 СССР  
Дата: 31.03.17 10:26
Оценка: 5 (1)
Здравствуйте, uzhas, Вы писали:

U>что-то я не вижу тут проблем, можно разжевать?

U>разве выражение std::move(f)(1) не эквивалентно f(1) ?

Может быть не эквивалентно, если f — функциональный объект с перегруженным operator() для rvalue (как здесь функция-член mf3).
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: Вызов функтора для элементов переменного списка парам
От: uzhas Ниоткуда  
Дата: 31.03.17 11:00
Оценка:
Здравствуйте, rg45, Вы писали:

R>Может быть не эквивалентно, если f — функциональный объект с перегруженным operator() для rvalue (как здесь функция-член mf3).


мда, очередная свежая экзотика, решающая одни проблемы и создающая другие
Отредактировано 31.03.2017 11:08 uzhas . Предыдущая версия . Еще …
Отредактировано 31.03.2017 11:02 uzhas . Предыдущая версия .
Re[8]: Вызов функтора для элементов переменного списка парам
От: niXman Ниоткуда https://github.com/niXman
Дата: 31.03.17 11:09
Оценка:
Здравствуйте, uzhas, Вы писали:

U>всё же в исходной критике речь, видимо, о чем-то другом

в бессмысленности такой записи:
template<typename F, typename A0, typename A1>
void func(F &&f, A0 &&a0, A1 &&a1) {
   std::move(f)(std::forward<A0>(a0));
   std::move(f)(std::forward<A1>(a1));
}
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.