La razón de que esto no es así porque cuando se especifica foo<Movable>
, la función que está uniéndose a es:
void foo(Movable&&) // *must* be an rvalue
{
}
Sin embargo, el valor pasado por std::bind
no habrá un valor de lado derecho, pero un lvalue (almacenado como miembro en algún lugar del functor resultante bind
). Eso, es el funtor 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í se puede ver esto falla porque Movable&&
No se puede enlazar a Movable
†
Una solución sencilla podría ser esto en su lugar:
auto f = std::bind(foo<Movable&>, Movable());
Porque ahora la función que está llamando es:
void foo(Movable& /* conceptually, this was Movable& &&
and collapsed to Movable& */)
{
}
Y la llamada funciona bien (y, por supuesto, puede hacer ese foo<const Movable&>
si lo desea). Sin embargo, una cuestión interesante es si podemos conseguir su vinculación original con el trabajo, y que puede a través de:
auto f = std::bind(foo<Movable>,
std::bind(static_cast<Movable&&(&)(Movable&)>(std::move<Movable&>),
Movable()));
Es decir, que sólo std::move
el argumento antes de hacer la llamada, por lo que se puede unir. Pero bueno, eso es feo. El molde es necesario porque std::move
es una función sobrecargada, por lo que debemos especificar qué sobrecarga queremos mediante conversión al tipo deseado, eliminando las otras opciones.
En realidad no sería tan malo si std::move
no estaba 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 así, creo que está claro que probablemente solo quiera especificar un argumento de plantilla más explícito.
† Esto es diferente a llamar a la función sin argumentos plantilla explícita, ya que especifica explícitamente que elimina la posibilidad de que pueda ser deducida. (T&&
, donde T
es un parámetro de plantilla, se puede deducir nada a , if you let it be.)
Gracias. De hecho, estoy tratando de hacer lo último (pasando el valor r a foo como valor r para habilitar la semántica de movimiento). Sin embargo, esperaba algo así como un 'std :: ref' para referencias de valores; el enlace de tu última solución es demasiado largo, y el enlace anidado te distrae. – Timothy003
@Timothy: Bueno, tener un rvalue 'int' realmente no es tan útil, es solo un' int'. ¿Estás trabajando con un tipo más complejo? Si es así, tal vez debas editar tu pregunta para dejar en claro lo que estás esperando. (Si es el caso, creo que puede haber un malentendido sobre el uso canónico de la semántica de movimientos; no se preocupe, definitivamente no es muy conocido todavía) – GManNickG
@GMan Pero no debería ser posible implementar std :: bind así, que usa std :: forward internamente para pasar rvalue si él mismo tiene rvalue? ¿No es eso un error en la implementación de std :: bind o en la especificación (sigue siendo borrador, por lo que tiene sentido notificar errores, ¿no?) –