Это потому, что константная ссылка может связываться как с lvalue, так и с rvalue. Это имеет смысл, поскольку помечено как const, значение не может быть изменено, даже если входное значение является r-значением. Если значение не является константным, у вас есть возможность редактировать неконстантную ссылку внутри функции. Это не имело бы никакого смысла, поскольку входные данные были r-значением.
Если ничего другого, const_iterator
читает лучше, так как он говорит всем, кто читает код «Я просто перебираю этот контейнер, не перебирая содержащиеся в нем объекты».
Это огромная победа, не говоря уже о различиях в производительности.
Я не понимаю, почему это так - константность - это проверка времени компиляции. Но очевидный ответ - написать тест.
Редактировать: Вот мой тест - он дает идентичные тайминги на моей машине:
#include <vector>
#include <iostream>
#include <ctime>
using namespace std;;
int main() {
vector <int> v;
const int BIG = 10000000;
for ( int i = 0; i < BIG; i++ ) {
v.push_back( i );
}
cout << "begin\n";
int n = 0;
time_t now = time(0);
for ( int a = 0; a < 10; a++ ) {
for( vector <int>::iterator it = v.begin(); it != v.end(); ++it ) {
n += *it;
}
}
cout << time(0) - now << "\n";
now = time(0);
for ( int a = 0; a < 10; a++ ) {
for( vector <int>::const_iterator cit = v.begin(); cit != v.end(); ++cit ) {
n += *cit;
}
}
cout << time(0) - now << "\n";;
return n != 0;
}
По моему опыту, компилятор не выполняет какую-либо измеримую оптимизацию при использовании константных итераторов. Несмотря на то, что утверждение «это могло» верно, и ссылки не определены как указатели в стандарте.
Однако, у вас может быть две ссылки на один и тот же объект, так что одна может быть const, одна не const. Затем, я думаю, ответы в этого потока на ограничительные указатели применимы: компилятор не может знать, был ли объект изменен, например, другим потоком или некоторым кодом обработки прерываний.
контейнер
возвращает const T &
вместо T &
, поэтому компилятор может выполнять обычные оптимизации для константных объектов.
В наших рекомендациях по кодированию сказано, что предпочитают const_iterator
. Прочтите статью Скотта Мейерса здесь . Он объясняет, почему следует предпочесть итератор const_iterator.
The guideline we use is:
Always prefer const over non-const
If you tend to use const object, you get used to using only constant operations on the objects you get and that is as much as using const_iterator as much as possible.
Constness has a viral property. Once you get to use it, it propagates to all your code. Your non-mutating methods become constant, and that requires using only constant operations on the attributes, and passing constant references around, that itself forces only constant operations...
To me, the performance advantage of using constant iterators over non constant iterators (if any at all) is much less important than the improvement in the code itself. Operations meant (designed) to be non-mutating are constant.
Они предназначены для нетривиальных контейнеров / итераторов. Разберитесь со своими привычками, и вы не потеряете работоспособность, когда это будет иметь значение.
Кроме того, есть несколько причин предпочесть const_iterator, несмотря ни на что:
begin ()
, чтобы пометить данные как грязные (например, OpenSG) и отправить их другим потокам / по сети при синхронизации, так что имеет реальные последствия для производительности. В качестве примера последнего пункта выше, вот отрывок из qmap.h в Qt:
inline iterator begin() { detach(); return iterator(e->forward[0]); }
inline const_iterator begin() const { return const_iterator(e->forward[0]); }
Даже если итератор и const_iterator практически эквивалентны (за исключением const
),
detach ()
создает новую копию данных, если его используют два или более объекта. Это совершенно бесполезно, если вы собираетесь просто читать данные, которые вы указываете с помощью const_iterator
.
Конечно, есть точки данных и в другом направлении:
«Константность», как и ограничение доступа (публичный, защищенный, приватный), приносит пользу программисту больше, чем помогает в оптимизации.
Компиляторы не могут на самом деле оптимизировать для такого количества ситуаций, в которых используется const, как можно подумать, по многим причинам (например, const_cast, изменяемые члены данных, псевдонимы указателей / ссылок). Однако наиболее важной причиной здесь является то, что тот факт, что const_iterator не позволяет изменять данные, на которые он ссылается, не означает, что эти данные нельзя изменить другими способами. И если компилятор не может определить, что данные доступны только для чтения, он не сможет оптимизировать намного больше, чем это было бы для случая неконстантного итератора.
Дополнительную информацию и примеры можно найти по адресу: http://www.gotw.ca/gotw/081.htm