В C++ 11, std::function
is MoveConstructible, то есть можно реально вызвать std::move
на таких объектах или сохранить их в подвижных типах. Недостаток: что должен напечатать следующий код?Должен ли std :: function :: operator bool возвращать false после перемещения?
#include <stdio.h>
#include <functional>
#include <utility>
struct Big {
char data[1024];
};
int main(int argc, char **argv) {
Big blob;
// This bind will trigger small object optimization
std::function<void()> little = std::bind([]() { printf("little\n"); });
// This bind will not
std::function<void()> big = std::bind([](Big const& b) {
printf("big %c\n", b.data[0]);
}, blob);
auto little_moved = std::move(little);
auto big_moved = std::move(big);
// After move, one expects the source std::function to be empty
// (boolean value false)
printf("Little empty: %d\n", !little);
printf("Little (moved) empty: %d\n", !little_moved);
printf("Big empty: %d\n", !big);
printf("Big (moved) empty: %d\n", !big_moved);
return 0;
}
Собран с GCC 4.8, вы получите это:
linux-dev:nater:/tmp$ g++-4.8 -g -o foo move_function.cc -std=c++11
linux-dev:nater:/tmp$ ./foo
Little empty: 1
Little (moved) empty: 0
Big empty: 1
Big (moved) empty: 0
объект ведет себя, как и ожидалось, недействительности RHS о назначении перемещения. Однако, не все так ясно, с лязгом (Apple LLVM версии 6.0):
workbrick:nater:/tmp$ clang++ -g -o foo move_function.cc -std=c++11 -stdlib=libc++
workbrick:nater:/tmp$ ./foo
Little empty: 0
Little (moved) empty: 0
Big empty: 1
Big (moved) empty: 0
Здесь RHS аннулируется (ложь в логическом контексте) после того, как двигаться, когда связанные параметры велики, но не тогда, когда связаны параметры являются небольшими (технически, несуществующими). Рассматривая реализацию <functional>
поставляется с Xcode, мы видим, что поведение различается в зависимости от того, была ли применена небольшая оптимизация объекта:
template<class _Rp, class ..._ArgTypes>
template <class _Alloc>
function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&,
function&& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
else if (__f.__f_ == (__base*)&__f.__buf_)
{
// [nater] in this optimization, __f.__f_ is not invalidate
__f_ = (__base*)&__buf_;
__f.__f_->__clone(__f_);
}
else
{
// [nater] here, the RHS gets invalidated
__f_ = __f.__f_;
__f.__f_ = 0;
}
}
Теперь я знаю, что состояние ОРЗ после присвоения перемещения является тип- но я удивил, что поведение этого STL-класса несовместимо. Это действительно неопределенное в спецификации?
Действительно, так оно и есть. Для потомков: _ (6) effects: If! F, * у этого нет цели; в противном случае move-конструирует цель из f в цель * этого, оставляя f в допустимом состоянии с неопределенным значением ._ Не стесняйтесь распространять это на ответ, и я его приму. –