Стандарт c ++ ограничивает арифметику указателей, выполняемую в массиве ( [expr.add] ), что затрудняет реализацию вектороподобных контейнеров.
Можно реализовать вектор-как контейнер с реализацией, подобной этой:
//First approach
//Allocation
auto buffer = new unsigned char[2*sizeof(int)];
//Construction
auto p=new(buffer) int{};
new(p+1) int{};
//Example of use of an iterator, assign 10 to the second element.
*(p+1)=10;//UB p+1 is a pointer past the end of an object.
Этот предыдущий фрагмент кода иллюстрирует, как приблизительно std::vector
реализован в libstdc ++ и libc ++. кажется , что компиляторы принимают этот вид кода как расширение языка c ++.
Если я хочу быть совместимым со стандартом, я мог бы реализовать vector
и связанные с ним iterator
втаким образом, что операции, выполняемые над вектором и его итератором, могут быть упрощены до следующего кода:
//Second approach
//Allocation:
auto buffer = new unsigned char[2*sizeof(int)];
//Construction
new(buffer) int{};
new(buffer+sizeof(int)) int{};
//Example of use of an iterator assign 10 to the second element
*(std::launder(reinterpret_cast<int*>(buffer+sizeof(int))))=10;
(Первый вопрос, не является ли этот подход также UB? Здесь арифметика указателей выполняется над массивом без знакаchar, который обеспечивает хранение для объектов int. launder
используется, потому что buffer
и объекты int
не являются взаимозаменяемыми указателями)
Проблема с этим вторым подходом - это код, сгенерированный компилятором (GCC):
#include <new>
int test_approach_1(unsigned char* buffer){
//Construction
auto p = new(buffer) int{};
new(p+1) int{10};
//Example of use of an iterator assign 10 to the second element
*(p+1)=13;//UB
return *(p+1);//UB
}
int test_approach_2(unsigned char* buffer){
//Construction
new(buffer) int{};
new(buffer+sizeof(int)) int{10};
//Example of use of an iterator assign 10 to the second element
*(std::launder(reinterpret_cast<int*>(buffer+sizeof(int))))=13;
return *(std::launder(reinterpret_cast<int*>(buffer+sizeof(int))));
}
Генерируемая сборка:
test_approach_1(unsigned char*):
movabs rax, 55834574848
mov QWORD PTR [rdi], rax
mov eax, 13
ret
test_approach_2(unsigned char*):
movabs rax, 42949672960
mov QWORD PTR [rdi], rax
mov eax, 13
mov DWORD PTR [rdi+4], 13
ret
Код, сгенерированный для test_approach_1
, является оптимальным.Поэтому я думаю, что я не буду использовать второй подход (и у меня была бы еще одна причина не использовать его, если кто-то показывает, что это также UB.)
Я не нахожу документацию для этих расширений языка, которыйПозвольте нам реализовать векторные контейнеры, используя первый подход (это UB в соответствии со стандартом).Есть ли документация для этого?На каком компиляторе можно ожидать его работы и с какими флагами компилятора?