Май std::vector использует небольшую оптимизацию буфера?
Мне было интересно с моим коллегой сегодня, можно ли реализовать std::vector, чтобы использовать небольшую оптимизацию буфера. Просмотрев проект С++ 11, я прочитал в 23.3.1p8
Выражение a.swap(b) для контейнеров a и b стандартного типа контейнера, отличного от массива, должно обменивать значения a и b без вызова операций перемещения, копирования или свопинга для отдельных элементов контейнера.
Вначале кажется, что он запрещает оптимизацию небольших буферов, но в соответствии с правилом as-if нам будет разрешено выполнять небольшую оптимизацию буфера для типов неклассов (поскольку мы не можем наблюдать за выполняемой копией). Следующий текст кажется сложнее "обмануть"
Каждый итератор, ссылающийся на элемент в одном контейнере перед свопом, должен ссылаться на тот же элемент в другом контейнере после свопинга.
Достаточно ли этого, чтобы предотвратить реализацию оптимизации малого буфера для std::vector? Есть ли какие-либо другие дорожные блоки или в конечном итоге возможно иметь std::vector с SBO?
Ответы
Ответ 1
23.2.1/p10/b6:
Если не указано иное...
- Функция
- no swap() делает недействительными любые ссылки, указатели или итераторы, ссылающиеся на элементы обменяемых контейнеров....
Нигде он не указывает "иначе" для vector
. Таким образом, это запрещает SBO для vector
.
string
не связан этим правилом, потому что он "указывает иначе" в 21.4.1/p6:
Ссылки, указатели и итераторы, относящиеся к элементам basic_string может быть признана недействительной следующими способами использования этого Объект basic_string:
- как аргумент любой стандартной библиотечной функции, ссылающейся на не-const basic_string в качестве аргумента. ^ 234
234) Например, в качестве аргумента для функций, не являющихся членами, swap() (21.4.8.8), оператор → () (21.4.8.9) и getline() (21.4.8.9) или как аргумент basic_string:: swap()
Ответ 2
В дополнение к проблеме с недействительностью итератора есть аргумент безопасности, чтобы избежать оптимизации небольшого буфера.
Если запись превысит std::vector
, вы получите повреждение кучи, что довольно сложно предсказать, что перезаписывается и очень сложно использовать для выполнения произвольного кода.
Если буфер вместо этого встроен в локальную переменную, переполнение перепутает стек, и злоумышленник, вероятно, получит контроль над обратным адресом, что гораздо более полезно (например, атаки с возвратом в libc).