c++ - Pasando valores a través de std:: bind
c++11 rvalue-reference (5)
(Esto es en realidad un comentario a la respuesta de GMan, pero necesito algún formato para el código). Si el funtor generado en realidad es así:
struct your_bind { your_bind(Movable arg0) : arg0(arg0) {} void operator()() { foo(arg0); } Movable arg0; };
entonces
int main() { auto f = your_bind(Movable()); f(); // No errors! }
Cumple sin errores. ya que es posible asignar e inicializar datos con rvalue y luego pasar un valor de datos a rvalue argumento del foo ().
Sin embargo, supongo que la implementación de enlace extrae el tipo de argumento de función directamente de la firma foo (). Es decir, el funtor generado es:
struct your_bind { your_bind(Movable && arg0) : arg0(arg0) // **** Error:cannot convert from Movable to Movable && {} void operator()() { foo(arg0); } Movable&& arg0; };
y de hecho, esto realmente falla al inicializar el miembro de datos de valor. Tal vez, la implementación de vinculación simplemente no extrae correctamente el tipo "sin referencia" del tipo de argumento de función y utiliza este tipo para la declaración del miembro de datos del functor "tal como está", sin recortar &&.
El funtor correcto debe ser:
struct your_bind { your_bind(Movable&& arg0) : arg0(arg0) {} void operator()() { foo(arg0); } Movable arg0; // trim && !!! };
Quiero pasar un rvalue a través de std::bind
a una función que toma una referencia rvalue en C ++ 0x. No puedo averiguar cómo hacerlo. Por ejemplo:
#include <utility>
#include <functional>
template<class Type>
void foo(Type &&value)
{
Type new_object = std::forward<Type>(value); // move-construct if possible
}
class Movable
{
public:
Movable(Movable &&) = default;
Movable &operator=(Movable &&) = default;
};
int main()
{
auto f = std::bind(foo<Movable>, Movable());
f(); // error, but want the same effect as foo(Movable())
}
Chicos, he pirateado una versión de reenvío perfecta de una carpeta (limitada a 1 param) aquí http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html
Para referencia el código es
template <typename P>
class MovableBinder1
{
typedef void (*F)(P&&);
private:
F func_;
P p0_;
public:
MovableBinder1(F func, P&& p) :
func_(func),
p0_(std::forward<P>(p))
{
std::cout << "Moved" << p0_ << "/n";
}
MovableBinder1(F func, P& p) :
func_(func),
p0_(p)
{
std::cout << "Copied" << p0_ << "/n";
}
~MovableBinder1()
{
std::cout << "~MovableBinder1/n";
}
void operator()()
{
(*func_)(std::forward<P>(p0_));
}
};
Como se puede ver en la prueba de concepto anterior, es muy posible ...
No veo ninguna razón por la cual std :: bind sea incompatible con std :: move ... std :: forward es, después de todo, un reenvío perfecto. No entiendo por qué no hay un std :: forwarding_bind ???
La razón por la que esto falla es porque cuando especifica foo<Movable>
, la función a la que está enlazando es:
void foo(Movable&&) // *must* be an rvalue
{
}
Sin embargo, el valor pasado por std::bind
no será un valor de r, sino un valor de l (almacenado como miembro en algún lugar del functor de vinculación resultante). Eso, es el functor generado es similar a:
struct your_bind
{
your_bind(Movable arg0) :
arg0(arg0)
{}
void operator()()
{
foo<int>(arg0); // lvalue!
}
Movable arg0;
};
Construido como your_bind(Movable())
. Así que puedes ver que esto falla porque Movable&&
no se puede enlazar con Movable
. †
Una solución simple podría ser esta en su lugar:
auto f = std::bind(foo<Movable&>, Movable());
Porque ahora la función que estás llamando es:
void foo(Movable& /* conceptually, this was Movable& &&
and collapsed to Movable& */)
{
}
Y la llamada funciona bien (y, por supuesto, puede hacer que foo<const Movable&>
si lo desea). Pero una pregunta interesante es si podemos lograr que su enlace original funcione, y podemos hacerlo a través de:
auto f = std::bind(foo<Movable>,
std::bind(static_cast<Movable&&(&)(Movable&)>(std::move<Movable&>),
Movable()));
Es decir, simplemente std::move
el argumento antes de hacer la llamada, para que pueda vincularse. Pero sí, eso es feo. La conversión se requiere porque std::move
es una función sobrecargada, por lo que tenemos que especificar qué sobrecarga queremos al lanzar el tipo deseado, eliminando las otras opciones.
En realidad, no sería tan malo si std::move
no estuviera sobrecargado, como si tuviéramos algo como:
Movable&& my_special_move(Movable& x)
{
return std::move(x);
}
auto f = std::bind(foo<Movable>, std::bind(my_special_move, Movable()));
Que es mucho más simple. Pero a menos que tenga una función de este tipo, creo que está claro que probablemente solo desee especificar un argumento de plantilla más explícito.
† Esto es diferente a llamar a la función sin un argumento de plantilla explícito, porque especificarlo de manera explícita elimina la posibilidad de que se deduzca. ( T&&
, donde T
es un parámetro de plantilla, se puede deducir de cualquier cosa , si lo dejas ).
Podrías usar una expresión lambda.
auto f = [](){ foo(Movable()); };
Esta parece ser la opción más simple.
Una mejora más en la respuesta de GManNickG y tengo una bonita solución:
auto f = std::bind(
foo<Movable>,
std::bind(std::move<Movable&>, Movable())
);
(trabaja en gcc-4.9.2 y msvc2013)