Почему я не могу хранить ссылки на STL-карте в С++?
Я недооцениваю, что ссылки не являются указателями, а псевдонимами для объекта. Тем не менее, я до сих пор не понимаю, что именно это означает для меня как программиста, т.е. Что такое ссылки под капотом?
Я думаю, что лучший способ понять это - понять, почему я не могу хранить ссылку на карте.
Я знаю, что мне нужно перестать думать о ссылках как синтаксический грузинг по указателям, просто не уверен, как:/
Ответы
Ответ 1
Как я понимаю, ссылки реализуются как указатели под капотом. Причина, по которой вы не можете хранить их на карте, является чисто семантической; вам нужно инициализировать ссылку, когда она создана, и вы больше не сможете ее изменить. Это не связано с тем, как работает карта.
Ответ 2
Вы должны думать о ссылке как "указатель const к неконстантному объекту":
MyObject& ~~ MyObject * const
Кроме того, ссылка может быть построена только как псевдоним того, что существует (что необязательно для указателя, хотя и целесообразно, кроме NULL). Это не гарантирует, что объект будет оставаться вокруг (и действительно, у вас может быть ядро при доступе к объекту через ссылку, если его больше нет), рассмотрите этот код:
// Falsifying a reference
MyObject& firstProblem = *((MyObject*)0);
firstProblem.do(); // undefined behavior
// Referencing something that exists no more
MyObject* anObject = new MyObject;
MyObject& secondProblem = *anObject;
delete anObject;
secondProblem.do(); // undefined behavior
Теперь для контейнера STL существует два требования:
- T должен быть конструктивным по умолчанию (ссылка не указана)
- T должен быть назначен (вы не можете reset ссылку, хотя вы можете назначить ее рефери)
Итак, в контейнерах STL вам нужно использовать прокси или указатели.
Теперь использование указателей может оказаться проблематичным для обработки памяти, поэтому вам может потребоваться:
НЕ используйте auto_ptr, возникает проблема с назначением, так как он изменяет правый операнд.
Надеюсь, это поможет:)
Ответ 3
Важное отличие от синтаксического сахара заключается в том, что ссылки не могут быть изменены, чтобы ссылаться на другой объект, чем тот, с которым они были инициализированы. Вот почему они не могут храниться на картах или в других контейнерах, потому что контейнеры должны иметь возможность изменять тип элемента, который они содержат.
В качестве иллюстрации этого:
A anObject, anotherObject;
A *pointerToA=&anObject;
A &referenceToA=anObject;
// We can change pointerToA so that it points to a different object
pointerToA=&anotherObject;
// But it is not possible to change what referenceToA points to.
// The following code might look as if it does this... but in fact,
// it assigns anotherObject to whatever referenceToA is referring to.
referenceToA=anotherObject;
// Has the same effect as
// anObject=anotherObject;
Ответ 4
Контейнер, в котором хранится ссылка , имеет, чтобы инициализировать все свои элементы при построении и, следовательно, менее полезен.
struct container
{
string& s_; // string reference
};
int main()
{
string s { "hello" };
//container {}; // error - object has an uninitialized reference member
container c { s }; // Ok
c.s_ = "bye";
cout << s; // prints bye
}
Кроме того, после инициализации хранение элементов контейнера не может быть изменено. s_ будет всегда ссылаться на хранилище s выше.
Ответ 5
на самом деле вы можете использовать ссылки на карте. я не рекомендую это для больших проектов, так как это может вызвать странные ошибки компиляции, но:
map<int, int&> no_prob;
int refered = 666;
no_prob.insert(std::pair<int, int&>(0, refered)); // works
no_prob[5] = 777; //wont compile!!!
//builds default for 5 then assings which is a problem
std::cout << no_prob[0] << std::endl; //still a problem
std::cout << no_prob.at(0) << std::endl; //works!!
поэтому вы можете использовать карту, но будет сложно гарантировать, что она будет использоваться правильно, но я использовал ее для небольших кодов (обычно конкурентоспособных).
Ответ 6
В этом сообщении объясняется, как указатели реализуются под капотом - http://www.codeproject.com/KB/cpp/References_in_c__.aspx, который также поддерживает ответы sebastians.