не должны ли оба поведения быть одинаковыми или оба (доступ по индексу и at ()) дают ненормальное завершение или нормально завершаются?
Нет, они не должны иметь одинаковое поведение,Поведение преднамеренно отличается.Если бы это было не так, то существовал бы только один из них.
Функция-член at
выполняет проверку границ.Любой доступ за пределы контейнера приводит к исключению.Это то же самое, что и функция-член at
, например, std::array
или std::vector
.Обратите внимание, что необработанный бросок приведет к завершению программы.
Оператор индекса не выполняет никаких проверок за пределами.До C ++ 11 любой доступ к элементам с индексами > size()
имел неопределенное поведение.Ни при каких обстоятельствах оператор индекса не может выдать исключение.Это аналогично оператору индекса массива, например std::array
или std::vector
.
Начиная с C ++ 11, поведение оператора индекса std::string
было изменено так, что чтение элементапо индексу == size()
(т. е. один после последнего элемента) четко определен и возвращает нулевой терминатор.Только изменение объекта через возвращенную ссылку имеет неопределенное поведение.Чтение других индексов за пределами границ по-прежнему ведет к неопределенному поведению.
Я не знаю, на самом деле, чтобы не вносить соответствующее изменение в at
, чтобы разрешить доступ к нулевому терминатору, но я подозреваю, что это считалосьбыть назад несовместимым изменением.Правильное определение UB всегда обратно совместимо, а прекращение исключения - нет.Другая возможная причина состоит в том, что он открыл бы маршрут к UB (если нулевой терминатор был изменен), и конструкция at
заключается в том, чтобы освободить его от UB.