Почему операторы стандартной библиотеки C++ принимают rvalue?

Когда я запускаю следующий код:

std::string myString = "I'm a string.";
const std::string::iterator &myIterator = ++myString.begin();
char c = *myIterator;
std::cout << c << std::endl;

Я получаю ошибку сегментации (компиляция с оптимизацией O3). Я предполагаю, что это связано с тем, что оператор ++ возвращает std::string::iterator &, а не std::string::iterator, и поэтому мы получаем ссылку на временное. Есть ли причина, по которой это не реализовано, чтобы вызвать ошибку компиляции? То есть, почему подпись не следующая?

std::string::iterator &std::string::iterator::operator++() &;

Или, что еще лучше, почему спецификация не требует следующих подписей, чтобы мы могли без проблем обрабатывать rvalue?

std::string::iterator &std::string::iterator::operator++() &;
std::string::iterator std::string::iterator::operator++() &&;

person Jeremy B.    schedule 31.05.2016    source источник
comment
Невозможно воспроизвести. Вы уверены, что это из-за этих строк? Кстати, даже если ++ возвращает ссылку, myIterator не является ссылкой, поэтому значение будет скопировано.   -  person Rakete1111    schedule 31.05.2016
comment
Это работает нормально. Можете ли вы опубликовать полный код?   -  person Sumeet    schedule 31.05.2016
comment
Да, ты прав. Я изменил пример, и теперь он должен падать.   -  person Jeremy B.    schedule 31.05.2016
comment
Я не уверен, как предложенный вами набор сигнатур функций решит проблему - назначение любой из них ссылке все равно приведет к оборванной ссылке. Как вы упомянули в комментарии, компилятору было бы неплохо выдать предупреждение о назначении временного объекта, который будет немедленно уничтожен по ссылке. Но это действительно отдельная тема.   -  person Michael Burr    schedule 31.05.2016
comment
Мне кажется, что второй вариант сработает, поскольку, когда мы привязываем значение r к ссылке, время жизни временного объекта продлевается.   -  person Jeremy B.    schedule 31.05.2016
comment
Я согласен с тем, что включение этих перегрузок позволило бы избежать ошибки... но делать это последовательно для всей стандартной библиотеки было бы кошмаром и, возможно, также внести критические изменения в существующий код. Вы, вероятно, застряли в том, чтобы помнить об осторожности при объявлении константных ссылок (а также, нет никаких причин использовать константную ссылку на итератор - они разработаны, чтобы быть легковесными и передавать по значению).   -  person M.M    schedule 01.06.2016
comment
IMO, позволяющее использовать ++ для prvalue, в первую очередь, немного подозрительно, встроенный ++ можно использовать только для lvalue. Но многие люди, кажется, довольны этим.   -  person M.M    schedule 01.06.2016
comment
На самом деле это деталь реализации относительно того, компилируется ли этот код вообще: библиотека может использовать char * в качестве строкового итератора.   -  person M.M    schedule 01.06.2016
comment
Спасибо; это проясняет это. Я понял, почему мое первое предложение может привести к критическим изменениям (скажем, если бы мы сделали char c = *(++myString.begin())), но ваши два других комментария эффективно демонстрируют, как предложенная мной идиома приведет к несогласованности.   -  person Jeremy B.    schedule 01.06.2016
comment
См. комментарии для stackoverflow.com/questions/31359829/ для дальнейшего обсуждения этой темы.   -  person Jeremy B.    schedule 18.06.2016
comment
Никакого минимального воспроизводимого примера тогда не было   -  person Slava    schedule 26.08.2016


Ответы (2)


Код, который вы разместили, кажется мне законным: std::string::iterator myIterator = ++myString.begin(); копирует ссылку на итератор в myIterator, чтобы не было оборванных ссылок на временные объекты.

Мои экстрасенсорные способности к отладке говорят мне, что в вашем реальном коде есть одна из двух проблем:

  • Ваша строка на самом деле пуста, даже если вы думаете, что в ней есть текст. В этом случае ++begin() недействителен.
  • Вы изменяете строку после получения копии итератора таким образом, что итератор становится недействительным.
person Mark B    schedule 31.05.2016
comment
Мое экстрасенсорное предсказание состоит в том, что пример кода должен был быть std::string::iterator& myIterator = ++myString.begin();, и в этом случае на самом деле есть оборванная ссылка. - person Michael Burr; 31.05.2016
comment
Хороший колл (хотя изначально у меня тоже был const). Я все еще не уверен, почему это должно успешно компилироваться. - person Jeremy B.; 31.05.2016

Комментарии @M.M проницательны. В частности, мое второе предложение ограничило бы возможности библиотек при реализации итераторов, поскольку итераторы не обязаны увеличиваться как rvalue (ссылка: http://en.cppreference.com/w/cpp/iterator/next), и на самом деле std::string::iterator может быть реализован как char *, который не увеличивается как rvalue.

Решение о том, следует ли следовать моему первому предложению, похоже, является решением реализации библиотеки, поскольку стандарт предписывает, что итераторы могут делать, а не то, что они не могут, чтобы не ограничивать без необходимости то, какие типы могут функционировать как итераторы. Конечно, на этом этапе изменение реализации может привести к критическим изменениям в пользовательском коде.

person Jeremy B.    schedule 26.08.2016