Как избежать случайного получения элемента массива по значению, а не по ссылке?

Скажи у меня есть массив std::vector<Foo>и я хочу перебрать все мои foos и делать что-то вроде этого:

for (auto foo : vecFoo)
foo.x = 10;

Это в конечном итоге ничего не делает, потому что он делает локальную копию содержимого vecFoo вместо ссылки на него. Правильный цикл выглядит следующим образом:

for (auto& foo : vecFoo)
foo.x = 10;

Это ошибка, которую я совершил несколько раз, поэтому я хотел бы найти решение, которое поймает меня, когда я ошибаюсь. Я был бы счастлив, что-то, что я могу сделать со структурой, или флаг предупреждения, который я могу включить. Я пытался сделать FooКопирую конструктор приватный, но потом я не могу сделать push_back или же emplace_backчто явно не то, что я хочу.

2

Решение

Ответ на основную проблему учусь. Если вы совершите ошибку еще пару раз, вы в конечном итоге будете учиться на собственном опыте.

Что касается языковых трюков для вашего конкретного случая, когда у вас есть полный контроль над типом, который хранится в контейнере, вы можете предоставить nothrow переместить конструктор и отключить копирование конструкции. Это потребует использования emplace_back (или же переместить семантику с push_back).

Тем не менее, это только способ вызвать ошибку в данном конкретном случае, но если вы храните любой тип, который не находится на 100% под вашим контролем (то есть не может отключить конструкцию копирования или добавить конструкцию перемещения или не может реализовать конструктор перемещения nothrow — не могу гарантировать, что его не выбросят) тогда вам не повезло, что приводит к первому предложению: научиться пользоваться ссылками

4

Другие решения

Это ошибка, которую я совершил несколько раз, поэтому я хотел бы найти решение, которое поймает меня, когда я ошибаюсь.

По моему опыту, лучшее решение — научить себя всегда объявлять объекты как const по умолчанию. Это делает проверенной компилятором ошибку, чтобы попытаться изменить их. Как только вы это сделаете, компилятор поймает такое объявление для вас, когда вы забыли отклониться от него, чтобы сделать объект доступным для записи:

// This is what my hands write by default:
int const x = 42;

Я стараюсь всегда так писать. Затем всякий раз, когда я вижу следующее в коде:

int y =  42;

Мой разум пытается выяснить, должна ли переменная быть модифицируемой. Вы можете адаптировать это мышление к примеру с циклом, потому что, к счастью, C ++ 11 допускает это и для циклов range-for:

for (auto const i : vecFoo)
i.foo = 10; // error

Конечно, это работает только после того, как вы научились автоматически const все.

Я согласен, что это далеко от совершенства и требует значительных упражнений. Как неоднократно отмечали несколько человек, лучшим решением было бы для C ++ рассмотреть объявления const по умолчанию (и, по крайней мере, для лямбда-захваченных значений это это случай) но этот корабль уже отплыл.

1

То, о чем я прошу, — это сделать эту копию недействительной для моего класса, чтобы не было возможности совершить такую ​​ошибку.

C ++ 11 упрощает копирование вашего класса. Просто удалите конструктор копирования / оператор присваивания (при условии, что ваш компилятор поддерживает эту функцию C ++ 11):

class SomeClass
{
public:
SomeClass(const SomeClass &) = delete;
SomeClass &operator=(const SomeClass &) = delete;

...
};

Любая попытка вызвать конструктор копирования приведет к ошибке компилятора. Если вы используете Visual Studio, которая еще не поддерживает этот синтаксис, вам придется использовать стандартные идиомы C ++ 03 для этого. А именно, объявив конструктор копирования конфиденциально.


Я больше не могу использовать свою структуру в векторах, так как она не может быть скопирована в вектор.

Что вы можете. Вы просто не можете использовать функции, которые требуют копирование. Вы должны заменить свое использование push_back а также insert с emplace_back а также emplace,

Помните: если вы объявляете конструктор копирования и оператор присваивания, даже просто чтобы удалить их, вы должен вручную объявить переехать конструктор / оператор присваивания. Конечно, вы можете использовать = default синтаксис.

1
По вопросам рекламы [email protected]