Может ли лямбда-функция создать экземпляр функции шаблона?

#c #lambda #c 17 #template-specialization #c -experimental

Вопрос:

Вопросы, касающиеся использования универсальных лямбд C 14 или шаблонных лямбд C 20, обычно касаются создания лямбд с соответствующими параметризованными типами.

Мой вопрос в том, возможно ли для параметра lambda или его оценки принудительно создать экземпляр (или специализацию) шаблона, например, функции шаблона? Параметр (n) должен быть квалифицирован так constexpr , чтобы это работало.

 template <int n> ret_type fn (...) {...}
...
auto fx = [] (int n) { return fn<n>(...) }
 

Я не полностью в курсе C 20 или новых рабочих предложений и признаю, что все еще есть нюансы с constexpr и т. Д. В лямбда-выражениях C 17 и других функциях edge, которые заставляют меня cppreference довольно часто искать , Josuttis и других.

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

Комментарии:

1. параметры функции не могут быть constexpr такими, чтобы ваш конкретный пример не работал. Но я полагаю, что это помимо вопроса, который вы задаете?

2. @rubenvb — да. AFAIK, там нет concept / requires синтаксиса, подобного некоторым (constexpr int n) — я не вижу стандартного решения, но мне любопытны языковые предложения и тому подобное. Например, лямбды получают больше возможностей с каждым стандартом с момента их введения.

3. @BrettHale ты имеешь в виду, например consteval ?

4. @Mgetz — В некотором смысле. Если параметр специализации шаблона является consteval выражением. Мой вопрос начинает казаться все более и более непрактичным, когда я думаю об этом, учитывая ограничения, которые потребовались бы для такой гипотетической «лямбды»…

5. @BrettHale Я думаю, что вы, вероятно, задаете неправильный вопрос. Я бы посоветовал спросить о том, что вы пытаетесь сделать, а не о потенциальном решении. Здесь есть возможности (я делал сумасшедшие вещи std::integer_sequence ), но, не зная, что вы пытаетесь сделать, трудно дать хороший ответ.

Ответ №1:

Ответ на ваш вопрос технически да, лямбда-тела могут создавать экземпляры функций шаблона. Фактический пример не работает, потому int n что в качестве параметра его нельзя использовать таким образом.

Существует простой обходной путь

 template<auto x>
using constant_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
template<auto x>
constexpr constant_t<x> constant = {};

template <int n> int fn () { int arr[n] = {0}; return sizeof(arr); }
auto fx = [] (auto n) { return fn<n>(); };
std::cout << fx( constant<3> );
 

Живой пример.

Здесь я создал шаблон constant<x> переменной, который создает экземпляр std::integral_constant<X, x> . Это тип без состояния (но не бесполезный!), который имеет преобразование constexpr в его значение.

Мы можем передать это в лямбду, и до тех пор, пока лямбда принимает его по значению, мы можем затем преобразовать его в constexpr значение внутри лямбды, включая передачу его в качестве параметра, не относящегося к типу шаблона, создание экземпляра специализации функции шаблона, как вы просите.

Это можно сделать без шаблона constant переменной, т. е. если у вас нет auto поддержки параметров:

 template<std::size_t N>
using index_t = std::integral_constant<std::size_t, N>;
template<std::size_t N>
constexpr index_t<N> index = {};
 

мы можем использовать его версию для определенного типа и просто передать ее, и она работает точно так же.


В стороне, constant<?> это весело. Например:

 using upFILE=std::unique_ptr<
  std::FILE,
  constant_t<std::fclose>
>;

upFILE file( fopen("hello.txt", "r") );
 

поступает правильно, тм.

Комментарии:

1. Так вот как бы вы это сделали, не злоупотребляя std::integer_sequence хорошим…

2. Это довольно элегантно. Спасибо.

Ответ №2:

Ответ на заданный вопрос таков: да, тело лямбды может создавать экземпляры шаблонов.

Конкретный пример в вашем вопросе невозможен, так как параметры функции не могут быть constexpr , что необходимо для того, чтобы что-то использовалось в качестве параметра шаблона.

Вы можете использовать переменные constexpr, доступные из области определения лямбды:

 template<int N>
void something();

constexpr int N = 10;

int main()
{
    auto f = [] { something<N>(); };
    f();
}
 

Здесь приведен живой пример, в котором something экземпляр находится в теле выражения де лямбда. Но я не думаю, что это то, что вам нужно. Обратите внимание, что вызов лямбда-объекта не приводит (и никогда не мог привести) к созданию экземпляра шаблона.

Комментарии:

1. Спорный момент: вы можете сделать это, злоупотребляя пакетами параметров и std::integer_sequence . Не то чтобы я бы рекомендовал это делать.

2. Значение, которое вы передадите шаблону, все равно будет чем-то (косвенно) constexpr и никогда не сможет исходить, например, из пользовательского ввода. Это, на мой взгляд, ничем не отличается от использования, например, T::value_type, где T используется в качестве типа параметра шаблона функции.

3. не возражаю, но я не припомню, чтобы ОП говорил, что им нужна такая гибкость.