Рассмотрим случай шаблонной функции с переменными аргументами шаблона:
template<typename Tret, typename... T> Tret func(const T&... t);
Теперь у меня есть кортеж t
значений. Как мне вызвать, func()
используя значения кортежа в качестве аргументов? Я читал об bind()
объекте функции с call()
функцией, а также о apply()
функции в различных устаревших документах. Реализация GNU GCC 4.4, кажется, имеет call()
функцию в bind()
классе, но документации по этому вопросу очень мало.
Некоторые люди предлагают рукописные рекурсивные хаки, но истинное значение аргументов вариационных шаблонов - это возможность использовать их в случаях, подобных описанным выше.
У кого-нибудь есть решение или есть подсказка, где почитать об этом?
integer_sequence
см en.cppreference.com/w/cpp/utility/integer_sequenceinteger_sequence S
, вы просто вызываете свою функцию какfunc(std::get<S>(tuple)...)
и позволяете компилятору обрабатывать все остальное.Ответы:
Вот мой код, если кому-то интересно
В основном во время компиляции компилятор будет рекурсивно развернуть все аргументы в различных включающих вызовах функций <N> -> вызовов <N-1> -> вызовов ... -> вызовов <0>, который является последним, и компилятор будет оптимизировать различные промежуточные вызовы функций, чтобы сохранить только последний, который является эквивалентом func (arg1, arg2, arg3, ...)
Предусмотрено 2 версии: одна для функции, вызываемой для объекта, а другая для статической функции.
источник
tr1
вещи теперь можно удалить с помощью c ++ 11В C ++ 17 вы можете сделать это:
Это уже работает в Clang ++ 3.9, используя std :: эксперимент :: apply.
Отвечая на комментарий о том, что это не сработает, если
the_function
задан шаблон, можно обойти следующее:Этот обходной путь представляет собой упрощенное решение общей проблемы передачи наборов перегрузок и шаблона функции, где ожидалась бы функция. Общее решение (которое заботится о совершенной пересылке, constexpr-ness и noexcept-ness) представлено здесь: https://blog.tartanllama.xyz/passing-overload-sets/ .
источник
the_function
шаблонизирован.std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
В C ++ есть много способов развернуть / распаковать кортеж и применить эти элементы кортежа к функции шаблона variadic. Вот небольшой вспомогательный класс, который создает индексный массив. Он часто используется в шаблонном метапрограммировании:
Теперь код, который делает эту работу, не такой большой:
Тест показан ниже:
Я не большой специалист по другим языкам, но я полагаю, что если эти языки не имеют такой функциональности в своем меню, это невозможно сделать. По крайней мере, с C ++ вы можете, и я думаю, что это не так сложно ...
источник
template<class ... T> void three(T...) {}
и попробую использовать apply, он не скомпилируется.Я считаю, что это самое элегантное решение (и оно оптимально передается):
Пример использования:
К сожалению, GCC (по крайней мере, 4.6) не может скомпилировать это с помощью «извините, невыполнение: перегрузка искажения» (что просто означает, что компилятор еще не полностью реализует спецификацию C ++ 11), и поскольку он использует шаблоны с переменным числом аргументов, он не будет работать в MSVC, поэтому он более или менее бесполезен. Однако, как только будет найден компилятор, который поддерживает спецификацию, это будет лучшим подходом IMHO. (Примечание: это не так сложно изменить, чтобы вы могли обойти недостатки GCC или реализовать его с помощью Boost Preprocessor, но это нарушает элегантность, поэтому эту версию я публикую.)GCC 4.7 теперь прекрасно поддерживает этот код.
Редактировать: Добавлена пересылка вокруг фактического вызова функции для поддержки справочной формы rvalue * это в случае, если вы используете clang (или если кто-то еще действительно находит способ добавить его).
Редактировать: Добавлено пропущенное вперед по объекту функции в теле функции применения, не являющейся членом. Спасибо pheedbaq за то, что он указал, что его не хватает.
Редактировать: А вот версия C ++ 14, поскольку она намного лучше (на самом деле еще не компилируется):
Вот версия для функций-членов (не очень проверенная!):
источник
apply
функции,f
не являющейся членом , почему не заключено вstd::forward
вызов, как в возвращаемом типе? Разве это не нужно?foo('x', true)
скомпилировать с точно таким же кодом сборки, что иapply(foo, ::std::make_tuple('x', true))
с любым уровнем оптимизации, кроме -O0.integer_sequence
вы даже получаете почти правильную реализациюapply()
в своем примере. см. мой ответ ниже.Это адаптировано из проекта C ++ 14 с использованием index_sequence. Я мог бы предложить применить в будущем стандарте (TS).
источник
Новости не выглядят хорошими.
Прочитав только что выпущенный проект стандарта , я не вижу встроенного решения, которое кажется странным.
Лучшее место, чтобы спросить о таких вещах (если вы еще этого не сделали) - comp.lang.c ++. Moderated, потому что некоторые люди регулярно участвуют в составлении стандартного поста.
Если вы проверите эту тему , у кого-то возникнет тот же вопрос (может, это вы, и в этом случае вы найдете весь этот ответ немного расстраивающим!), И вам предложат несколько неприятных реализаций.
Мне просто интересно, будет ли проще заставить функцию принимать
tuple
, так как преобразование таким способом проще. Но это подразумевает, что все функции должны принимать кортежи в качестве аргументов для максимальной гибкости, и поэтому это просто демонстрирует странность отсутствия встроенного расширения кортежа в пакет аргументов функции.Обновление: ссылка выше не работает - попробуйте вставить это:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
источник
Все эти реализации хороши. Но из-за использования указателя на компилятор функции-члена часто невозможно встроить вызов целевой функции (по крайней мере, gcc 4.8 не может, несмотря ни на что. Почему gcc не может встроить указатели на функции, которые можно определить? )
Но все изменится, если отправить указатель на функцию-член как аргументы шаблона, а не как параметры функции:
И использование:
Доказательство inlinable http://goo.gl/5UqVnC
С небольшими изменениями мы можем «перегрузить»
apply_tuple
:Плюс это единственное решение, которое работает с шаблонными функциями.
источник
1) если у вас есть готовая структура parameter_pack в качестве аргумента функции, вы можете просто использовать std :: tie следующим образом:
2) если у вас нет готового парампака, вам придется раскрутить кортеж вот так
источник
Как насчет этого:
run_tuple
Шаблон функции принимает данный кортеж и передать его элементы по отдельности для данной функции. Он выполняет свою работу путем рекурсивного вызова своих шаблонов вспомогательных функцийexplode_tuple
. Важно, чтобыrun_tuple
размер кортежа передавалсяexplode_tuple
; это число действует как счетчик количества элементов для извлечения.Если кортеж пуст, то
run_tuple
вызывается первая версияexplode_tuple
с удаленной функцией в качестве единственного другого аргумента. Удаленная функция вызывается без аргументов, и мы закончили. Если кортеж не пустой, большее число передается второй версииexplode_tuple
вместе с удаленной функцией. Рекурсивный вызовexplode_tuple
создается с теми же аргументами, за исключением того, что число счетчиков уменьшается на единицу и (ссылка на) последний элемент кортежа добавляется в качестве аргумента после удаленной функции. В рекурсивном вызове либо счетчик не равен нулю, и выполняется другой вызов с уменьшенным счетчиком, и элемент со следующей ссылкой не вставляется в список аргументов после удаленной функции, но до того, как другие вставленные аргументы или счетчик достигает ноль и удаленная функция вызывается со всеми аргументами, накопленными после нее.Я не уверен, что у меня есть синтаксис принудительного использования определенной версии шаблона функции. Я думаю, что вы можете использовать указатель на функцию как функциональный объект; компилятор автоматически исправит это.
источник
Я оцениваю MSVS 2013RC, и в некоторых случаях не удалось собрать некоторые из предыдущих решений, предложенных здесь. Например, MSVS не сможет скомпилировать «автоматические» возвраты, если слишком много параметров функции, из-за ограничения на проникновение в пространство имен (я отправил эту информацию в Microsoft, чтобы исправить ее). В других случаях нам нужен доступ к возврату функции, хотя это также можно сделать с помощью лямды: следующие два примера дают одинаковый результат.
И еще раз спасибо тем, кто разместил здесь ответы до меня, я бы не добрался до этого без этого ... так вот оно:
источник
const
?Расширяя решение @ David, вы можете написать рекурсивный шаблон, который
integer_sequence
семантикуint N
для подсчета рекурсивных итерацийНапример:
В качестве альтернативы, если ваш функтор не определен во время компиляции (например,
constexpr
экземпляр без функтора или лямбда-выражение), вы можете использовать его как параметр функции вместо параметра шаблона класса и фактически полностью удалить содержащий класс:Для вызовов функции указателя на функцию-члена вы можете настроить любой из приведенных выше фрагментов кода так же, как в ответе @ David.
объяснение
Что касается второго фрагмента кода, есть две функции шаблона: первая принимает функтор
func
, кортежt
с типамиT...
и пакет параметровargs
типовArgs_tmp...
. При вызове он рекурсивно добавляет объекты изt
пакета параметров по одному, от начала (0
) до конца, и снова вызывает функцию с новым увеличенным пакетом параметров.Сигнатура второй функции практически идентична первой, за исключением того, что она использует тип
T...
для пакета параметровargs
. Таким образом, как толькоargs
первая функция будет полностью заполнена значениями изt
, ее тип будетT...
(в псевдо-кодеtypeid(T...) == typeid(Args_tmp...)
), и, таким образом, компилятор вместо этого вызовет вторую перегруженную функцию, которая, в свою очередь, вызываетfunc(args...)
.Код в примере статического функтора работает идентично, вместо этого функтор используется в качестве аргумента шаблона класса.
источник
Почему бы просто не обернуть свои переменные аргументы в класс кортежей, а затем использовать рекурсию времени компиляции (см. Ссылку ), чтобы получить интересующий вас индекс. Я считаю, что распаковка шаблонов переменных в контейнер или коллекцию может быть небезопасной по сравнению с гетерогенными типами
источник
Args...
->tuple
, ноtuple
->Args...
.Это простое решение работает для меня:
источник