Trong khi làm việc trên this question, tôi nhận thấy rằng việc thực hiện GCC (v4.7) của std::function
di chuyển các đối số của nó khi chúng được thực hiện theo giá trị. Các mã sau đây cho thấy hành vi này:Chức năng `std ::` có được phép di chuyển đối số của nó không?
#include <functional>
#include <iostream>
struct CopyableMovable
{
CopyableMovable() { std::cout << "default" << '\n'; }
CopyableMovable(CopyableMovable const &) { std::cout << "copy" << '\n'; }
CopyableMovable(CopyableMovable &&) { std::cout << "move" << '\n'; }
};
void foo(CopyableMovable cm)
{ }
int main()
{
typedef std::function<void(CopyableMovable)> byValue;
byValue fooByValue = foo;
CopyableMovable cm;
fooByValue(cm);
}
// outputs: default copy move move
Chúng ta thấy ở đây là một bản sao của cm
được thực hiện (trong đó có vẻ hợp lý kể từ khi tham số của s byValue
được lấy theo giá trị), nhưng sau đó có hai di chuyển. Kể từ khi function
hoạt động trên một bản sao của cm
, thực tế là nó di chuyển đối số của nó có thể được xem như là một chi tiết thực hiện không quan trọng. Tuy nhiên, hành vi này gây ra một số sự cố when using function
together with bind
:
#include <functional>
#include <iostream>
struct MoveTracker
{
bool hasBeenMovedFrom;
MoveTracker()
: hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker const &)
: hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker && other)
: hasBeenMovedFrom(false)
{
if (other.hasBeenMovedFrom)
{
std::cout << "already moved!" << '\n';
}
else
{
other.hasBeenMovedFrom = true;
}
}
};
void foo(MoveTracker, MoveTracker) {}
int main()
{
using namespace std::placeholders;
std::function<void(MoveTracker)> func = std::bind(foo, _1, _1);
MoveTracker obj;
func(obj); // prints "already moved!"
}
Hành vi này có được phép theo tiêu chuẩn không? std::function
có được phép di chuyển các đối số của nó không? Và nếu vậy, có bình thường khi chúng ta có thể chuyển đổi trình bao bọc được trả về bởi bind
thành một tham số by-value std::function
, mặc dù điều này kích hoạt hành vi không mong muốn khi xử lý nhiều lần xuất hiện của trình giữ chỗ?
Dường như với tôi rằng vấn đề là nhiều hơn với trình giữ chỗ hơn 'std :: function'. Cụ thể, thực tế là khi tạo một 'tie', di chuyển được sử dụng từ đối số ban đầu cho cả hai kết quả mong đợi. –
Thật thú vị, trình biên dịch Visual C++ 11 in "di chuyển bản sao mặc định" trong ví dụ đầu tiên và không in "đã được di chuyển!" trong lần thứ hai. Tôi tự hỏi nếu động thái bổ sung này có thể đến từ hoạt động bên trong của std :: chức năng và/hoặc chuyển tiếp hoàn hảo. –
@MatthieuM. Bạn có thể xây dựng? Tôi không quen thuộc với việc triển khai trình giữ chỗ. Nếu vấn đề xuất phát từ trình giữ chỗ, làm thế nào vấn đề không phát sinh khi sử dụng 'auto' để suy ra kiểu" bind-wrapper ", thay vì sử dụng' std :: function'? –