Я наткнулся на следующую проблему при использовании проверенной реализации glibcxx:
/usr/include/c++/4.8.2/debug/vector:159:error: attempt to self move assign.
Objects involved in the operation:
sequence "this" @ 0x0x1b3f088 {
type = NSt7__debug6vectorIiSaIiEEE;
}
Который я привел к этому минимальному примеру:
#include <vector>
#include <random>
#include <algorithm>
struct Type {
std::vector<int> ints;
};
int main() {
std::vector<Type> intVectors = {{{1}}, {{1, 2}}};
std::shuffle(intVectors.begin(), intVectors.end(), std::mt19937());
}
Отслеживая проблему, я обнаружил, что shuffle
хочет std::swap
элемент с собой. Поскольку Type
определяется пользователем и для него не задана специализация для std::swap
, используется значение по умолчанию, которое создает временное значение и использует operator=(&&)
для переноса значений:
_Tp __tmp = _GLIBCXX_MOVE(__a);
__a = _GLIBCXX_MOVE(__b);
__b = _GLIBCXX_MOVE(__tmp);
Поскольку Type
явно не дает operator=(&&)
, он по умолчанию реализуется "рекурсивно", применяя ту же операцию к своим членам.
Проблема возникает в строке 2 кода подкачки, где __a
и __b
указывают на тот же объект, который действует в коде __a.operator=(std::move(__a))
, который затем запускает ошибку в проверенной реализации vector::operator=(&&)
.
Мой вопрос: кто это виноват?
- Это мое, потому что я должен предоставить реализацию для
swap
, которая делает "self swap" aNOP
? - Это
std::shuffle
, потому что он не должен пытаться поменять элемент с собой? - Является ли это проверенной версией, потому что самовозвращение - отлично?
- Все правильно, проверенная реализация просто делает мне одолжение при выполнении этой дополнительной проверки (но тогда как отключить ее)?
Я читал о перетасовке, требующей, чтобы итераторы были ValueSwappable. Разве это распространяется на самостоятельную замену (что является простой проблемой времени выполнения и не может быть реализовано с помощью проверок концепции компиляции)?
Добавление
Чтобы вызвать ошибку более непосредственно, можно использовать:
#include <vector>
int main() {
std::vector<int> vectorOfInts;
vectorOfInts = std::move(vectorOfInts);
}
Конечно, это совершенно очевидно (почему вы переместили вектор в себя?).
Если вы заменили std::vector
, то ошибка не возникла бы из-за того, что векторный класс имеет собственную реализацию функции свопинга, которая не использует operator=(&&)
.