С учетом следующего кода:
struct Window{
void show();
//stuff
}w1, w2, w3;
struct Widget{
void show();
//stuff
}w4, w5, w6;
struct Toolbar{
void show();
//stuff
}t1, t2, t3;
Я хочу show
добавить несколько элементов:
for (auto &obj : {w3, w4, w5, t1})
obj.show();
Однако это не скомпилируется, так как std::initializer_list<T>
в for
-loop не может вывести T
, и на самом деле нет подходящего T
. Я не хочу создавать тип стирания типа из-за количества требуемого кода и ненужных служебных данных во время выполнения. Как правильно написать мой цикл так, чтобы тип obj
выводился для каждого элемента в концептуальном списке отдельно?
В современном С++ вы должны использовать fold выражения, чтобы "пропустить" ваши гетерогенные аргументы, применяя функцию-член:
auto Printer = [](auto&&... args) {
(args.show(), ...);
};
Printer(w1, w2, w3, w4, w5, w6, t1, t2, t3);
Подробнее об этом читайте в блоге
boost:: fusion - это потрясающе, но oldskool - он удовлетворяет недостатки С++ 03.
С++ 11 расширение вариационного шаблона для спасения!
#include <iostream>
struct Window{
void show() {
std::cout << "Window\n";
}
//stuff
}w1, w2, w3;
struct Widget{
void show() {
std::cout << "Widget\n";
}
//stuff
}w4, w5, w6;
struct Toolbar{
void show()
{
std::cout << "Toolbar\n";
}
//stuff
}t1, t2, t3;
template<class...Objects>
void call_show(Objects&&...objects)
{
using expand = int[];
(void) expand { 0, ((void)objects.show(), 0)... };
}
auto main() -> int
{
call_show(w3, w4, w5, t1);
return 0;
}
ожидаемый вывод:
Window
Widget
Widget
Toolbar
другой, более общий путь (требуется С++ 14):
// note that i have avoided a function names that look like
// one in the standard library.
template<class Functor, class...Objects>
void for_all(Functor&& f, Objects&&... objects)
{
using expand = int[];
(void) expand { 0, (f(std::forward<Objects>(objects)), 0)... };
}
называется так:
for_all([](auto& thing) { thing.show(); }, w3, w4, w5, t1);
boost::fusion
oldskool, но при этом вы используете приведение в стиле C. Двойные стандарты?
Другой вариант - использовать алгоритмы boost::tuple
или std::tuple
и boost::fusion::for_each
:
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>
boost::fusion::for_each(
boost::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), // by reference, not a copy
[](auto&& t) { t.show(); }
);
Просто из любопытства сравнил сгенерированный сборник метода Ричарда Ходжеса с приведенным выше. С gcc-4.9.2 -Wall -Wextra -std=gnu++14 -O3 -march=native
полученный код сборки идентичен.
На основе https://stackoverflow.com/questions/1198260/iterate-over-tuple это работает без создания дополнительной функции, повышения или наследования.
Заголовок:
#include <tuple>
#include <utility>
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(const std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(const std::tuple<Tp...>& t, FuncT f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &&, FuncT) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>&& t, FuncT f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(std::move(t), f);
}
.cpp
struct Window{
void show(){}
//stuff
}w1, w2, w3;
struct Widget{
void show(){}
//stuff
}w4, w5, w6;
struct Toolbar{
void show(){}
//stuff
}t1, t2, t3;
int main() {
for_each(std::tie(w3, w4, w5, t1), [](auto &obj){
obj.show();
});
}
w3, w4, w5, t1
при вызове make_tuple
. кажется, что копировать экземпляры просто для их печати - это слишком много. демонстрация
std::tie
, поэтому копий можно избежать. Исправлена.
Window
, Widget
и Toolbar
обмениваются общим интерфейсом, поэтому вы можете создавать абстрактный класс и наследовать от него другие классы:
struct Showable {
virtual void show() = 0; // abstract method
};
struct Window: Showable{
void show();
//stuff
}w1, w2, w3;
struct Widget: Showable{
void show();
//stuff
}w4, w5, w6;
struct Toolbar: Showable{
void show();
//stuff
}t1, t2, t3;
Затем вы можете создать массив указателей на Showable
и перебрать его:
int main() {
Showable *items[] = {&w3, &w4, &w5, &t1};
for (auto &obj : items)
obj->show();
}
.size()
. Но в целом вы правы, это традиционное решение.
std::bind
вызвать функции-члены в массив std::function<void()>
? Нет виртуального наследования на виджете не требуется.
Я рекомендую Boost.Hana, в котором ИМХО является лучшей и самой гибкой библиотекой мета-программирования шаблонов.
#include <boost/hana/ext/std/tuple.hpp>
#include <boost/hana.hpp>
namespace hana = boost::hana;
hana::for_each(std::tie(w3, w4, w5, t1), [](auto& obj) { obj.show(); });
std::tie
, но у меня нет времени, чтобы найти его прямо сейчас. Если я найду один, я обновлю.
Я думаю, что boost::variant
стоит упомянуть. Тем более, что он имеет шансы стать std::variant
в С++ 17.
int main()
{
std::vector<boost::variant<Window*, Widget*, Toolbar*>> items = { &w1, &w4, &t1 };
for (const auto& item : items)
{
boost::apply_visitor([](auto* v) { v->show(); }, item);
}
return 0;
}
std::tuple
уже находится в C ++ 11.
std::tuple
здесь?
Поздний ответ, но здесь общее решение с С++ 14, которое работает как boost::fusion::for_each
но не требует увеличения:
#include <tuple>
namespace detail {
template<typename Tuple, typename Function, std::size_t... Is>
void tuple_for_each_impl(Tuple&& tup, Function&& fn, std::index_sequence<Is...>) {
using dummy = int[];
static_cast<void>(dummy {
0, (static_cast<void>(fn(std::get<Is>(std::forward<Tuple>(tup)))), 0)...
});
}
}
template<typename Function, typename... Args>
void tuple_for_each(std::tuple<Args...>&& tup, Function&& fn) {
detail::tuple_for_each_impl(std::forward<std::tuple<Args...>>(tup),
std::forward<Function>(fn), std::index_sequence_for<Args...>{});
}
int main() {
tuple_for_each(std::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), [](auto&& arg) {
arg.show();
});
}
Если вы хотите достичь более или менее того же уровня без std::tuple
, вы можете создать однофункциональный вариант над кодом:
#include <utility>
template<typename Function, typename... Args>
void va_for_each(Function&& fn, Args&&... args) {
using dummy = int[];
static_cast<void>(dummy {
0, (static_cast<void>(fn(std::forward<Args>(args))), 0)...
});
}
int main() {
auto action = [](auto&& arg) { arg.show(); };
va_for_each(action, w1, w2, w3, w4, w5, w6, t1, t2, t3);
}
Недостатком второго примера является то, что он требует сначала указать функцию обработки, поэтому не имеет такого же вида, как хорошо известный std::for_each
. В любом случае с моим компилятором (GCC 5.4.0) с использованием уровня оптимизации -O2
они создают тот же сборку.
tuple
virtual void show()
, он также должен работать с.size()
и std-контейнерами.