dev - c++17
VersiĆ³n de solo movimiento de std:: function (1)
No, no hay una versión de solo movimiento de std::function
en la biblioteca estándar de C ++. (A partir de C ++ 14)
Los delegados más rápidos posibles son una implementación de una std::function
como clase que resulta ser más rápida que la mayoría de las implementaciones std::function
en muchas bibliotecas std
, y debería ser fácil incluir una versión para move
y copy
.
Envolver su objeto de función move
solo en un shared_ptr<F>
en una clase con un operator()
reenvío operator()
es otro enfoque.
Aquí hay un boceto de la task
:
template<class Sig>
struct task;
namespace details {
template<class Sig>
struct task_iimpl;
template<class R, class...Args>
struct task_iimpl<R(Args...)> {
virtual ~task_iimpl() {}
virtual R invoke(Args&&...args) const = 0;
};
template<class F, class Sig>
struct task_impl;
template<class F, class R, class...Args>
struct task_impl<F,R(Args...)>:
task_iimpl<R(Args...)>
{
F f;
template<class T>
task_impl(T&& t):f(std::forward<T>(t)) {}
virtual R invoke(Args&&...args) const override {
return f( std::forward<Args>(args...) );
}
};
template<class F, class...Args>
struct task_impl<F,void(Args...)>:
task_iimpl<void(Args...)>
{
F f;
template<class T>
task_impl(T&& t):f(std::forward<T>(t)) {}
virtual void invoke(Args&&...args) const override {
f( std::forward<Args>(args...) );
}
};
}
template<class R, class...Args>
struct task<R(Args...)> {
virtual ~task_iimpl() {}
R operator()(Args...args) const {
return pImpl->invoke(std::forward<Args>(args...));
}
explicit operator bool()const{ return static_cast<bool>(pImpl); }
task(task &&)=default;
task& operator=(task &&)=default;
task()=default;
// and now for a mess of constructors
// the rule is that a task can be constructed from anything
// callable<R(Args...)>, destroyable, and can be constructed
// from whatever is passed in. The callable feature is tested for
// in addition, if constructed from something convertible to `bool`,
// then if that test fails we construct an empty task. This makes us work
// well with empty std::functions and function pointers and other tasks
// that are call-compatible, but not exactly the same:
struct from_func_t {};
template<class F,
class dF=std::decay_t<F>,
class=std::enable_if_t<!std::is_same<dF, task>{}>,
class FR=decltype(std::declval<F const&>()(std::declval<Args>()...)),
std::enable_if_t<std::is_same<R, void>{} || std::is_convertible<FR, R>{} >*=0,
std::enable_if_t<std::is_convertible<dF, bool>{}>*=0
>
task(F&& f):
task(
static_cast<bool>(f)?
task( from_func_t{}, std::forward<F>(f) ):
task()
)
{}
template<class F,
class dF=std::decay_t<F>,
class=std::enable_if_t<!std::is_same<dF, task>{}>,
class FR=decltype(std::declval<F const&>()(std::declval<Args>()...)),
std::enable_if_t<std::is_same<R, void>{} || std::is_convertible<FR, R>{} >*=0,
std::enable_if_t<!std::is_convertible<dF, bool>{}>*=0
>
task(F&& f):
task( from_func_t{}, std::forward<F>(f) )
{}
task(std::nullptr_t):task() {}
// overload resolution helper when signatures match exactly:
task( R(*pf)(Args...) ):
task( pf?task( from_func_t{}, pf ):task() )
{}
private:
template<class F,
class dF=std::decay_t<F>
>
task(from_func_t, F&& f):
pImpl( std::make_unique<details::task_impl<dF,R(Args...)>>(
std::forward<F>(f)
)
{}
std::unique_ptr<details::task_iimpl<R(Args...)> pImpl;
};
pero no ha sido probado ni compilado, solo lo escribí.
Una versión de mayor resistencia industrial incluiría una pequeña optimización de buffer (SBO) para almacenar callables pequeños (suponiendo que sean móviles, si no móviles, almacenar en montón para permitir el movimiento), y un get-pointer-si-adivina-el- type-right (como std::function
).
Como std::function
es copiable, el estándar requiere que los callables utilizados para construir std :: function también puedan ser copiados:
n337 (20.8.11.2.1)
template<class F> function(F f);
Requiere:
F
debe ser CopyConstructible.f
debe ser invocable (20.8.11.2) para tipos de argumentosArgTypes
y return typeR
El constructor de copias y el destructor de A no lanzarán excepciones.
Esto implica que no es posible formar una función std :: a partir de un objeto de vinculación no copiable o una lambda que captura un tipo de solo movimiento como un único_ptr.
Parece posible implementar un contenedor de tan solo movimiento para call-only callables. ¿Existe un movimiento de biblioteca estándar, solo equivalente para std :: function? o, ¿hay una solución común para este problema?