Я хотел бы создать функцию, которая принимает слабый указатель и любой вид функтора (лямбда, std::function
, что угодно) и возвращает новый функтор, который выполняет оригинальный функтор только тогда, когда указатель не был удален за это время (поэтому давайте предположим, что есть WeakPointer
типа с такой семантикой). Все это должно работать для любого функтора без необходимости явно указывать сигнатуру функтора через параметры шаблона или приведение.
РЕДАКТИРОВАТЬ:
Некоторые комментаторы указали, что std::function
— который я использовал в своем подходе — может вообще не понадобиться, равно как и лямбда (хотя в своем первоначальном вопросе я также забыл упомянуть, что мне нужно захватить параметр слабого указателя), поэтому любое альтернативное решение, которое решает общую проблему это, конечно, также высоко ценится, может быть, я не думал достаточно нестандартно и был сосредоточен на использовании лямбда + std::function
, В любом случае, здесь идет то, что я пытался до сих пор:
template<typename... ArgumentTypes>
inline std::function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const std::function<void(ArgumentTypes...)>&& fun)
{
return [=] (ArgumentTypes... args)
{
if(pWeakPointer)
{
fun(args...);
}
};
}
Это хорошо работает без необходимости явно указывать типы аргументов, если я передаю std::function
, но терпит неудачу, если я передаю лямбда-выражение. Я думаю, это потому, что std::function
Неоднозначность конструктора, как в этот вопрос. В любом случае я попробовал следующий помощник, чтобы иметь возможность захватывать любые виды функций:
template<typename F, typename... ArgumentTypes>
inline function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const F&& fun)
{
return wrap(pWeakPointer, std::function<void(ArgumentTypes...)>(fun));
}
Теперь это работает для лямбд, которые не имеют параметров, но не работают для других, так как он всегда создает экземпляры ArgumentTypes...
с пустым набором.
Я могу придумать два решения проблемы, но не смог реализовать ни одно из них:
std::function
(или другой тип помощника Functor) создается для лямбды, то есть лямбда с подписью R(T1)
приводит к std::function(R(T1))
таким образом ArgumentTypes...
будет правильно выведенArgumentTypes...
в качестве параметра шаблона вместо этого есть какой-то другой способ (boost?), чтобы получить пакет аргументов из лямбда / функтора, так что я мог бы сделать что-то вроде этого:—
template<typename F>
inline auto wrap(WeakPointer pWeakPointer, const F&& fun) -> std::function<void(arg_pack_from_functor(fun))>
{
return wrap(pWeakPointer, std::function<void(arg_pack_from_functor(fun))(fun));
}
Предполагая, что слабый указатель занимает место первого аргумента, вот как я бы сделал это с общей лямбдой (с перехватами хода) и если бы C ++ позволил бы мне вернуть такую лямбду:
template<typename Functor, typename Arg, typename... Args>
auto wrap(Functor&& functor, Arg&& arg)
{
return [functor = std::forward<Functor>(functor)
, arg = std::forward<Arg>(arg)]<typename... Rest>(Rest&&... rest)
{
if(auto e = arg.lock()) {
return functor(*e, std::forward<Rest>(rest)...);
} else {
// Let's handwave this for the time being
}
};
}
Этот гипотетический код можно перевести в реальный код C ++ 11, если мы вручную «развернем» универсальную лямбду в полиморфный функтор:
template<typename F, typename Pointer>
struct wrap_type {
F f;
Pointer pointer;
template<typename... Rest>
auto operator()(Rest&&... rest)
-> decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )
{
if(auto p = lock()) {
return f(*p, std::forward<Rest>(rest)...);
} else {
// Handle
}
}
};
template<typename F, typename Pointer>
wrap_type<typename std::decay<F>::type, typename std::decay<Pointer>::type>
wrap(F&& f, Pointer&& pointer)
{ return { std::forward<F>(f), std::forward<Pointer>(pointer) }; }
Существует два простых варианта обработки случая, когда срок действия указателя истек: либо распространить исключение, либо вернуть внеполосное значение. В последнем случае тип возврата станет, например, optional<decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )>
а также // Handle
станет return {};
,
Пример кода, чтобы увидеть все в действии.
[Упражнение для амбициозного: улучшите код, чтобы можно было использоватьauto g = wrap(f, w, 4); auto r = g();
, Затем, если это еще не так, улучшите его так, чтобы auto g = wrap(f, w1, 4, w5);
также возможно и «делает правильные вещи». ]
Вам не нужно использовать лямбду.
#include <iostream>
#include <type_traits>
template <typename F>
struct Wrapper {
F f;
template <typename... T>
auto operator()(T&&... args) -> typename std::result_of<F(T...)>::type {
std::cout << "calling f with " << sizeof...(args) << " arguments.\n";
return f(std::forward<T>(args)...);
}
};
template <typename F>
Wrapper<F> wrap(F&& f) {
return {std::forward<F>(f)};
}
int main() {
auto f = wrap([](int x, int y) { return x + y; });
std::cout << f(2, 3) << std::endl;
return 0;
}