Мне нужно создать оболочку для лямбда-функции, которая будет принимать кортеж параметров исходной лямбда-функции и использовать ее реализацию. Я использую компилятор C++23. Это функция, которую я придумал:
template<class Fn>
constexpr auto wrap(Fn fn)
{
return [fn](typename lambda_traits<Fn>::Args&& t) { return std::apply(fn, std::forward<decltype(t)>(t)); };
}
Чтобы это работало, мне нужно создать класс lambda_traits
, экземпляр которого по типу лямбда выводит тип Args
, который представляет собой кортеж лямбда-параметров. Например, для лямбда-функции [](int i, int j){}
Args
будет tuple<int, int>
.
Поэкспериментировав с возможными решениями проблемы lambda_traits
, я, наконец, схитрил и делегировал выведение типов std::function
.
template<class Fn> struct lambda_traits;
template< class R, class ... A >
struct lambda_traits<R(A...)>
{
using Ret = R;
using Args = std::tuple<A...>;
constexpr lambda_traits(std::function<R(A...)>);
};
template< class R, class ... A >
lambda_traits(std::function<R(A...)>) -> lambda_traits<R(A...)>;
template<class Fn>
struct lambda_traits : decltype(lambda_traits(std::function(std::declval<Fn>())))
{
};
std::function
здесь похоже на клудж, я пытался заменить его, но не смог найти способ заставить его работать. Можете ли вы переработать это решение, удалив std::function
?
Еще одна проблема, неявные преобразования в некоторых случаях не происходят, возможно, где-то с типами что-то не так. Можете ли вы это исправить?
Это работает:
static_assert(
wrap([](std::string s) { return s.size();})(std::tuple{"abc"}) == 3);
Это не:
static_assert(
wrap([](const std::string& s) { return s.size();})(std::tuple{"abc"}) == 3);
Демо-версия Compiler Explorer находится здесь.
Вам действительно нужен typename lambda_traits<Fn>::Args&&
, разве вы не можете вместо него просто использовать auto&&
? это также решит вашу проблему с конверсией Демо.
@Jarod42 - вот что происходит, когда вы пытаетесь упростить вопрос, опуская детали. Функции переноса не обязательно знать тип кортежа, но мне нужен тип кортежа, потому что в реальном приложении это часть сервера, который знает, как выполнять транзакции только с кортежами.
Можете ли вы переработать это решение, удалив std::function?
Вы можете переопределить направляющие для std::function
, что несложно.
template<class Fn> struct lambda_traits ;
template< class R, class G, class ... A >
struct lambda_traits<R(G::*)(A...) const>
{
using Ret = R;
using Args = std::tuple<A...>;
};
template<class Fn>
struct lambda_traits : lambda_traits<decltype(&Fn::operator())>
{
};
Другая проблема: в некоторых случаях неявные преобразования не происходят. случаях, возможно, где-то что-то не так с типами. Можете ли вы исправить что?
Потому что вы не можете создать tuple<const std::string&>
из tuple<const char*>
, что привело бы к висячей ссылке, поэтому библиотека отклоняет ее.
Ах, я должен был догадаться использовать указатель на функцию-член. Спасибо.
Вы можете проверить, как
std::function
это определяет: en.cppreference.com/w/cpp/utility/functional/function/… Он определяет, что такое&Fn::operator()
, и выводит его аргументы — если он уникален, компилятор определяет его как указатель функции-члена и еще немного дополнительных свойств типов для определения аргументов.