В C ++ очень часто встречается то, что я считаю анти-паттерном, использующим const T&
как умный способ просто сказать T
при работе с параметрами. Однако значение и ссылка (независимо от того, является ли она константной или нет) - это две совершенно разные вещи, и всегда и вслепую использование ссылок вместо значений может привести к незначительным ошибкам.
Причина в том, что при работе со ссылками необходимо учитывать две проблемы, которых нет в значениях: время жизни и псевдонимы .
В качестве примера, одним из мест, где применяется этот анти-шаблон, является сама стандартная библиотека, где std::vector<T>::push_back
принимает в качестве параметра a const T&
вместо значения, и это может привести, например, к следующему коду:
std::vector<T> v;
...
if (v.size())
v.push_back(v[0]); // Add first element also as last element
Этот код является бомбой замедленного действия, потому что std::vector::push_back
хочет постоянную ссылку, но выполнение push_back может потребовать перераспределения, и если это произойдет, это означает, что после перераспределения полученная ссылка больше не будет действительной ( время жизни проблема), и вы входите в царство Неопределенное поведение.
Проблемы с псевдонимами также являются источником тонких проблем, если вместо значений используются ссылки на константы. Например, меня укусил код такого типа:
struct P2d
{
double x, y;
P2d(double x, double y) : x(x), y(y) {}
P2d& operator+=(const P2d& p) { x+=p.x; y+=p.y; return *this; }
P2d& operator-=(const P2d& p) { x-=p.x; y-=p.y; return *this; }
};
struct Rect
{
P2d tl, br;
Rect(const P2d& tl, const P2d& br) : tl(tl), bt(br) {}
Rect& operator+=(const P2d& p) { tl+=p; br+=p; return *this; }
Rect& operator-=(const P2d& p) { tl-=p; br-=p; return *this; }
};
Код кажется на первый взгляд довольно безопасным, P2d
- это двумерная точка, Rect
- это прямоугольник, а добавление / вычитание точки означает перевод прямоугольника.
Если, однако, перевести прямоугольник обратно в начало координат, вы пишете myrect -= myrect.tl;
, код не будет работать, потому что был определен оператор перевода, принимающий ссылку, которая (в этом случае) ссылается на член того же экземпляра.
Это означает, что после обновления топлифта с помощью tl -= p;
он будет равен (0, 0)
, как и должно быть, но также p
станет одновременно (0, 0)
, потому что p
- это просто ссылка на верхний левый член и поэтому обновление нижнего правого угла не будет работать, потому что оно переведет его на (0, 0)
, следовательно, ничего не делая.
Пожалуйста, не думайте, что ссылка на const похожа на значение из-за слова const
. Это слово существует только для того, чтобы сообщать вам об ошибках компиляции, если вы пытаетесь изменить ссылочный объект , используя эту ссылку , но не означает, что ссылочный объект является константой. Точнее говоря, объект, на который ссылается const ref, может измениться (например, из-за псевдоним ) и даже может прекратить существование, когда вы его используете ( продолжительность жизни проблема).
В const T&
слово const выражает свойство ссылки , а не ссылочного объекта : это свойство делает невозможным его использование изменить объект. Вероятно, readonly было бы лучшим именем, поскольку const имеет IMO психологический эффект от идеи, что объект будет постоянным, пока вы используете ссылку.
Конечно, вы можете добиться впечатляющего ускорения, используя ссылки вместо копирования значений, особенно для больших классов. Но при использовании ссылок вы всегда должны думать о проблемах с псевдонимами и сроками жизни, потому что под прикрытием они просто указывают на другие данные.
Однако для «нативных» типов данных (целые, двойные, указатели) ссылки на самом деле будут работать медленнее, чем значения, и выиграть от их использования вместо значений будет нечего.
Также константная ссылка всегда будет означать проблемы для оптимизатора, так как компилятор вынужден быть параноиком, и каждый раз, когда выполняется любой неизвестный код, он должен предполагать, что все ссылочные объекты теперь могут иметь различное значение (const
для ссылки для оптимизатора абсолютно НИЧЕГО, это слово предназначено только для помощи программистам - лично я не уверен, что это такая большая помощь, но это другая история).