Столкнулся недавно с ситуацией, что класс делал копирование вместо перемещения, т.к. одним из полей класса было не перемещаемое поле, что-то вроде такого:
UPD: пример рабочий, оставлено для истории. Демонстрируется в обсуждениях.
struct __attribute__((packed, aligned(1))) Token {
char abc = 0;
};
struct Data {
char *allocatedData = nullptr;
Data(Data &&o) : allocatedData(std::exchange(o.allocatedData, nullptr) {}
Data &operator=(Data &&o) { swap(allocatedData, o.allocatedData); }
// плюс еще конструкторы/деструкторы и присвоение
// которые реально выделяют память
};
...
// в таком pair этот Data перестает быть перемещаемым, только копируемым.
// и в результате при вставках в список для каждого сдвигаемого
// элемента происходит копирование, а не перемещение.
MyList<pair<Token,Data>> listSortedByToken;
При дальнейшем расследовании оказалось, что std::is_move_constructible && std::is_move_assignable сообщают true на такой pair, т.к. оно по прежнему содержит конструктор копирования. Да и на сам Token так же сообщают true.
Вопрос, можно ли как-либо идентифицировать, что что объект будет перемещаться, а не копироваться при использовании move/swap? Что бы можно было поставить на это static_assert.
UPD:
В общем, данный пример оказался нормально рабочим, а у меня раннее не работало из-за запутанности объявлений с =default && =delete у присвоений и конструкторов.
Но вопрос остается, можно ли как гарантировать, что объект будет перемещаться, а не копироваться? При том, что у объекта должны быть и конструкторы копирования. Что бы выполнение std::move строго вызывало конструкцию Data(Data&&), независимо от того, что move применен не к самому Data, а скажем к классу который инкапсулирует Data где-нибудь. Т.е. теперь это вопрос к классу Data, что бы он мог инкапсулироваться только в те классы, которые позволят ему быть перемещаемым.
=============================
UPD: В общем нашелся такой вот метод:
template <typename T, bool P>
struct is_movecopy_helper;
template <typename T>
struct is_movecopy_helper<T, false> {
typedef T type;
};
template <typename T>
struct is_movecopy_helper<T, true> {
template <typename U>
struct Dummy : public U {
Dummy(const Dummy&) = delete;
Dummy(Dummy&&) = default;
};
typedef Dummy<T> type;
};
template <class T>
struct has_move_constructor
: std::integral_constant<bool,
std::is_move_constructible<
typename is_movecopy_helper<T, std::is_class<T>::value>::type
>::value> { };
static_assert(has_move_constructor<T>::value);
Вроде работате, взят отсюда и немного подкорректирован: https://stackoverflow.com/questions/7054952/type-trait-for-moveable-types