Как я могу перебирать кортеж (используя C ++ 11)? Я пробовал следующее:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
но это не работает:
Ошибка 1: извините, не реализовано: невозможно развернуть "Listener ..." в список аргументов фиксированной длины.
Ошибка 2: я не могу появиться в постоянном выражении.
Итак, как мне правильно перебирать элементы кортежа?
Ответы:
Boost.Fusion - это возможность:
Непроверенный пример:
источник
У меня есть ответ, основанный на итерации по кортежу :
Обычная идея - использовать рекурсию времени компиляции. Фактически, эта идея используется, чтобы сделать printf безопасным по типу, как указано в исходных документах по кортежу.
Это можно легко обобщить до
for_each
кортежей:Хотя тогда это требует некоторых усилий, чтобы
FuncT
представить что-то с соответствующими перегрузками для каждого типа, который может содержать кортеж. Это работает лучше всего, если вы знаете, что все элементы кортежа будут иметь общий базовый класс или что-то подобное.источник
enable_if
документацию .for_each
. Фактически, я сделал это сам. :-) Думаю, этот ответ был бы более полезным, если бы он был уже обобщенным.const std::tuple<Tp...>&
.. Если вы не собираетесь изменять кортежи во время итерации, этихconst
версий будет достаточно.В C ++ 17, вы можете использовать
std::apply
с кратным выражения :Полный пример печати кортежа:
[Интернет-пример на Coliru]
Это решение решает вопрос о порядке оценки в ответе М. Алаггана .
источник
((std::cout << args << '\n'), ...);
? Лямбда вызывается один раз с элементами кортежа, распакованными какargs
, но что случилось с двойными круглыми скобками?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
.В C ++ 17 это можно сделать:
Это уже работает в Clang ++ 3.9 с использованием std :: experimental :: apply.
источник
do_something()
- происходит в неопределенном порядке, потому что пакет параметров раскрывается в вызове функции()
, в которой аргументы имеют неопределенный порядок? Это может быть очень важно; Я предполагаю, что большинство людей ожидают, что упорядочение будет гарантированно происходить в том же порядке, что и члены, то есть как индексы дляstd::get<>()
. AFAIK, чтобы получить гарантированный заказ в подобных случаях, расширение должно выполняться внутри{braces}
. Я ошибся? В этом ответе акцент делается на таком порядке: stackoverflow.com/a/16387374/2757035Используйте Boost.Hana и общие лямбды:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
источник
using namespace boost::fusion
(особенно вместе сusing namespace std
). Теперь нет никакого способа узнать , что лиfor_each
этоstd::for_each
илиboost::fusion::for_each
C ++ вводит операторы расширения для этой цели. Первоначально они были на пути к C ++ 20, но чуть не попали в обрез из-за нехватки времени на пересмотр формулировок языка (см. Здесь и здесь ).
В настоящее время согласованный синтаксис (см. Ссылки выше):
источник
Более простой, интуитивно понятный и удобный для компилятора способ сделать это в C ++ 17, используя
if constexpr
:Это рекурсия времени компиляции, аналогичная той, что представлена @emsr. Но это не использует SFINAE, поэтому (я думаю) он более удобен для компилятора.
источник
Вам нужно использовать метапрограммирование шаблона, как показано здесь с Boost.Tuple:
В C ++ 0x
print_tuple()
вместо этого вы можете написать функцию вариативного шаблона.источник
Сначала определите несколько помощников индекса:
С помощью вашей функции вы хотите применить к каждому элементу кортежа:
ты можешь написать:
Или, если
foo
возвращаетсяvoid
, используйтеПримечание. В C ++ 14
make_index_sequence
уже определено ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Если вам нужен порядок оценки слева направо, подумайте о следующем:
источник
foo
к, чтобы избежать возможной патологической перегрузки оператора.void
operator,
Вот простой способ C ++ 17 для перебора элементов кортежа с помощью стандартной библиотеки:
Пример:
Вывод:
Это можно расширить, чтобы условно разорвать цикл в случае, если вызываемый объект возвращает значение (но все еще работает с вызываемыми объектами, которые не возвращают присваиваемое значение типа bool, например void):
Пример:
Вывод:
источник
Если вы хотите использовать std :: tuple и у вас есть компилятор C ++, который поддерживает вариативные шаблоны, попробуйте приведенный ниже код (протестирован с g ++ 4.5). Это должен быть ответ на ваш вопрос.
boost :: fusion - еще один вариант, но для него требуется собственный тип кортежа: boost :: fusion :: tuple. Давайте лучше придерживаться стандарта! Вот тест:
сила вариативных шаблонов!
источник
В MSVC STL есть функция _For_each_tuple_element (не задокументирована):
источник
Другие упомянули несколько хорошо продуманных сторонних библиотек, к которым вы можете обратиться. Однако, если вы используете C ++ без этих сторонних библиотек, следующий код может помочь.
Примечание: код компилируется с любым компилятором, поддерживающим C ++ 11, и сохраняет согласованность с дизайном стандартной библиотеки:
Кортеж не обязательно должен быть
std::tuple
, и вместо этого может быть все, что поддерживаетstd::get
иstd::tuple_size
; в частности,std::array
иstd::pair
могут быть использованы;Кортеж может быть ссылочного типа или cv-квалифицированным;
Он ведет себя так же, как и
std::for_each
, и возвращает вводUnaryFunction
;Для пользователей C ++ 14 (или более поздней версии)
typename std::enable_if<T>::type
иtypename std::decay<T>::type
может быть заменен их упрощенной версией,std::enable_if_t<T>
иstd::decay_t<T>
;Для пользователей C ++ 17 (или более поздней версии)
std::tuple_size<T>::value
может быть заменен его упрощенной версией,std::tuple_size_v<T>
.Для пользователей C ++ 20 (или более поздней версии) эта
SFINAE
функция может быть реализована с помощьюConcepts
.источник
Используя
constexpr
andif constexpr
(C ++ 17), это довольно просто и понятно:источник
Я мог бы опоздать на этот поезд, но это будет здесь для справки в будущем.
Вот моя конструкция, основанная на этом ответе и по сути :
Затем вы используете его следующим образом:
Здесь есть место для улучшений.
Согласно коду OP, это станет следующим:
источник
Из всех ответов, которые я видел здесь, здесь и здесь , мне больше всего понравился способ итерации @sigidagi . К сожалению, его ответ очень многословен, что, на мой взгляд, затемняет присущую ему ясность.
Это моя версия его решения, которая более лаконична и работает с
std::tuple
,std::pair
иstd::array
.Демо: coliru
C ++ 14
std::make_index_sequence
может быть реализован для C ++ 11 .источник
Кортеж обеспечивает поднимать торг вспомогательных функций
get_head()
иget_tail()
поэтому ваши вспомогательные функции могут выглядеть следующим образом :как описано здесь http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
с
std::tuple
ним должно быть похоже.На самом деле, к сожалению
std::tuple
, похоже, не предоставляет такой интерфейс, поэтому методы, предложенные ранее, должны работать, или вам нужно будет переключиться на то,boost::tuple
что имеет другие преимущества (например, уже предоставленные операторы io). Хотяboost::tuple
у gcc есть и обратная сторона - он еще не принимает вариативные шаблоны, но это, возможно, уже исправлено, так как на моем компьютере не установлена последняя версия boost.источник
Я наткнулся на ту же проблему при переборе кортежа функциональных объектов, поэтому вот еще одно решение:
Вывод:
источник
Другой вариант - реализовать итераторы для кортежей. Это имеет то преимущество, что вы можете использовать различные алгоритмы, предоставляемые стандартной библиотекой, и циклы for на основе диапазона. Элегантный подход к этому объясняется здесь https://foonathan.net/2017/03/tuple-iterator/ . Основная идея заключается в том, чтобы превратить кортежи в ряд с
begin()
иend()
методами для обеспечения итераторы. Сам итератор возвращает,std::variant<...>
который затем можно посетить с помощьюstd::visit
.Вот несколько примеров:
Моя реализация (которая во многом основана на пояснениях в приведенной выше ссылке):
Доступ только для чтения также поддерживается путем передачи
const std::tuple<>&
вto_range()
.источник
Расширяя ответ @Stypox, мы можем сделать их решение более универсальным (начиная с C ++ 17). Добавив аргумент вызываемой функции:
Затем нам нужна стратегия для посещения каждого типа.
Начнем с некоторых помощников (первые два взяты из cppreference):
variant_ref
используется, чтобы разрешить изменение состояния кортежей.Использование:
Результат:
Для полноты картины вот мои
Bar
&Foo
:источник