これが失敗する理由は、を指定するfoo<Movable>
と、バインドする関数が次のようになるためです。
void foo(Movable&&) // *must* be an rvalue
{
}
ただし、渡される値はstd::bind
右辺値ではなく、左辺値(結果のbind
ファンクターのどこかにメンバーとして格納されます)になります。つまり、生成されたファンクターは次のようなものです。
struct your_bind
{
your_bind(Movable arg0) :
arg0(arg0)
{}
void operator()()
{
foo<int>(arg0); // lvalue!
}
Movable arg0;
};
として構築されyour_bind(Movable())
ます。Movable&&
にバインドできないため、これが失敗することがわかりますMovable
。†</ p>
代わりに、簡単な解決策は次のようになります。
auto f = std::bind(foo<Movable&>, Movable());
これで、呼び出している関数は次のようになります。
void foo(Movable& /* conceptually, this was Movable& &&
and collapsed to Movable& */)
{
}
そして、呼び出しは正常に機能します(もちろん、必要に応じて呼び出すこともできfoo<const Movable&>
ます)。しかし、興味深い質問は、元のバインドを機能させることができるかどうかです。
auto f = std::bind(foo<Movable>,
std::bind(static_cast<Movable&&(&)(Movable&)>(std::move<Movable&>),
Movable()));
つまりstd::move
、呼び出しを行う前に引数を指定するだけなので、バインドできます。しかし、うん、それは醜いです。std::move
はオーバーロードされた関数であるため、キャストが必要です。したがって、他のオプションを削除して、目的のタイプにキャストすることにより、必要なオーバーロードを指定する必要があります。
std::move
過負荷になっていなければ、次のようなものがあったかのように、実際にはそれほど悪くはありません。
Movable&& my_special_move(Movable& x)
{
return std::move(x);
}
auto f = std::bind(foo<Movable>, std::bind(my_special_move, Movable()));
これははるかに簡単です。しかし、そのような関数を配置していない限り、おそらくもっと明示的なテンプレート引数を指定したいだけであることは明らかだと思います。
†これは、明示的に指定すると推定される可能性がなくなるため、明示的なテンプレート引数なしで関数を呼び出すこととは異なります。(T&&
、ここT
で、はテンプレートパラメータですが、それを許可すれば、何にでも推測できます。)