- Есть ли способ написать compile_time_for так, чтобы он принимал шаблонную функцию в качестве первого аргумента?
Краткий ответ: нет.
Длинный ответ: шаблонная функция не является объектом, это набор объектов, и вы можете передать функции в качестве аргумента объект, а не набор объектов.
Обычное решение этой проблемыТип проблемы заключается в том, чтобы обернуть функцию шаблона внутри класса и передать объект класса (или просто тип, если функция обернута как статический метод).Это именно то решение, которое вы приняли в своем рабочем коде.
Если вопрос 1. положительный, есть ли издержки в первом рабочем коде из-за того, что подпрограмма создает объект типа OperatorType на каждой итерации цикла?
Вопрос 1 отрицательный.
Планируется ли ввести такую функцию, как время компиляции для цикла, в грядущем c ++ 20?
Я недостаточно знаю C ++ 20, чтобы ответить на этот вопросвопрос, но я полагаю, не передавая набор функций.
В любом случае, вы можете сделать своего рода время компиляции для цикла, используя std::make_index_sequence
/ std::index_sequence
начиная с C ++ 14.
Например, если вы согласитесь извлечь значение touple вне вашей функции myprint()
, вы можете заключить его в лямбду и написать что-нибудь следующим образом (используя также сворачивание шаблона C ++ 17; в C ++ 14 немного большесложный)
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <typename T>
void myprint (T const & t)
{ std::cout << t << " "; }
template <std::size_t start, std::size_t ... Is, typename F, typename ... Ts>
void ctf_helper (std::index_sequence<Is...>, F f, std::tuple<Ts...> const & t)
{ (f(std::get<start + Is>(t)), ...); }
template <std::size_t start, std::size_t end, typename F, typename ... Ts>
void compile_time_for (F f, std::tuple<Ts...> const & t)
{ ctf_helper<start>(std::make_index_sequence<end-start>{}, f, t); }
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>([](auto const & v){ myprint(v); }, x);
return 0;
}
Если вы действительно хотите извлечь элемент кортежа (или элементы кортежей) внутри функции, лучшее, что я могу себе представить, это преобразовать ваш первый пример следующим образом
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <std::size_t start, template <std::size_t> class OT,
std::size_t ... Is, typename... Args>
void ctf_helper (std::index_sequence<Is...> const &, Args && ... args)
{ (OT<start+Is>{}(std::forward<Args>(args)...), ...); }
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }
template <std::size_t I>
struct print_tuple_i
{
template <typename ... U>
void operator() (std::tuple<U...> const & x)
{ std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0u, 3u, print_tuple_i>(x);
return 0;
}
- РЕДАКТИРОВАТЬ -
ОП спрашивает
Есть ли какое-то преимущество использования index_sequence над моим первым кодом?
IЯ не эксперт, но таким образом вы избегаете рекурсии.С точки зрения шаблона, у компиляторов есть пределы рекурсии, которые могут быть строгими.Таким образом вы избегаете их.
Кроме того, ваш код не компилируется, если вы установите параметры шаблона end > start
.(Можно представить ситуацию, когда вы хотите, чтобы компилятор вообще определил, был ли создан цикл)
Я полагаю, вы имеете в виду, что мой код не компилируется, если start > end
.
Плохая часть в том, что здесь нет проверки этой проблемы, поэтому компилятор пытается скомпилировать мой код и в этом случае;поэтому встречайте
std::make_index_sequence<end-start>{}
, где end - start
- отрицательное число, но используемое шаблоном, ожидающим число без знака.Так что end - start
становится очень большим положительным числом, и это может вызвать проблемы.
Вы можете избежать этой проблемы, налагая static_assert()
внутри compile_time_for()
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{
static_assert( end >= start, "start is bigger than end");
ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...);
}
Или, возможно, вы можете использоватьSFINAE для отключения функции
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
std::enable_if_t<(start <= end)> compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }
Если вы хотите, с помощью SFINAE вы можете добавить перегруженную compile_time_for()
версию для управления end < start
кейсом
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename ... Args>
std::enable_if_t<(start > end)> compile_time_for (Args && ...)
{ /* manage the end < start case in some way */ }