Проблема, которую я пытаюсь решить, возникает при создании контейнеров, таких как std::vector
объектов, содержащих ссылочные и константные элементы данных:
struct Foo;
struct Bar {
Bar (Foo & foo, int num) : foo_reference(foo), number(num) {}
private:
Foo & foo_reference;
const int number;
// Mutable member data elided
};
struct Baz {
std::vector<Bar> bar_vector;
};
Это не будет работать как есть, потому что оператор присваивания по умолчанию для класса Foo
не может быть построен из-за ссылочного члена foo_reference
и константного члена number
.
Одно из решений - заменить это foo_reference
на указатель и избавиться от ключевого слова const
. Однако при этом теряются преимущества ссылок перед указателями, и этот член const
действительно должен быть const
. Это частные члены, поэтому единственное, что может навредить, - это мой собственный код, но я выстрелил себе в ногу (или выше) своим собственным кодом.
Я видел решения этой проблемы в Интернете в виде swap
методов, которые, кажется, переполнены неопределенным поведением, основанным на чудесах reinterpret_cast
и const_cast
. Бывает, что эти методы действительно работают на моем компьютере. Сегодня. С одной конкретной версией одного конкретного компилятора. Завтра или с другим компилятором? Кто знает. Я не собираюсь использовать решение, основанное на неопределенном поведении.
Связанные ответы на stackoverflow:
- Имеет ли смысл реализовать оператор копирования-присваивания в классе со всеми константными членами-данными?
В первом ответе есть забавная строка: «Если этот неизменяемый, значит, вы облажались». - Метод обмена с константными членами
Первый ответ здесь не применим, а второй - немного путаница.
Итак, есть ли способ написать swap
конструктор метода / копии для такого класса, который не вызывает неопределенное поведение, или я просто облажался?
Изменить
Чтобы прояснить ситуацию, я уже хорошо знаком с этим решением:
struct Bar {
Bar (Foo & foo, int num) : foo_ptr(&foo), number(num) {}
private:
Foo * foo_ptr;
int number;
// Mutable member data elided
};
Это явно исключает const
сущность number
и устраняет подразумеваемую const
сущность foo_reference
. Это не то решение, которое мне нужно. Если это единственное решение, отличное от UB, пусть будет так. Я также хорошо осведомлен об этом решении:
void swap (Bar & first, Bar & second) {
char temp[sizeof(Bar)];
std::memcpy (temp, &first, sizeof(Bar));
std::memcpy (&first, &second, sizeof(Bar));
std::memcpy (&second, temp, sizeof(Bar));
}
а затем написать оператор присваивания с использованием копирования и замены. Это позволяет обойти проблемы с ссылками и константами, но действительно ли это UB? (По крайней мере, он не использует reinterpret_cast
и const_cast
.) Некоторые из исключенных изменяемых данных представляют собой объекты, содержащие std::vector
, поэтому я не знаю, будет ли здесь работать такая неглубокая копия.
const
- и ссылочными членами бытьnoncopyable
? По крайней мере, это то, что я узнал. Тем не менее,move
-swap должен работать нормально, но это не C ++ 03. - person Xeo   schedule 28.09.2011