2016-10-20 8 views
10

Рассмотрим следующее, упрощены и неполно, внедрение фиксированного размера вектора:Каким образом std :: putder влияет на контейнеры?

template<typename T> 
class Vec { 
    T *start, *end; 

public: 
    T& operator[](ssize_t idx) { return start[idx]; } 

    void pop() { 
    end--; 
    end->~T(); 
    } 

    template<typename... U> 
    void push(U... args) { 
    new (end) T { std::forward<U>(args)... }; 
    end++; 
    } 
}; 

Теперь рассмотрим следующую Т:

struct T { 
    const int i; 
}; 

И следующий случай использования:

Vec<T> v; 
v.push(1); 
std::cout << v[0].i; 
v.pop(); 
v.push(2); 
std::cout << v[0].i; 

Оператор индекса использует указатель start для доступа к объекту. Объект в этой точке был уничтожен pop, а другой объект был создан в своем хранилище на push(2). Если я правильно прочитал документацию, связанную с std::launder, это означает, что поведение v[0] в строке ниже не определено.


Как следует использовать std :: washmer для исправления этого кода? Нужно ли начинать и заканчивать отмывание каждый раз, когда используется новое место? Текущие реализации stdlib, похоже, используют код, аналогичный тому, который был выше. Не определено ли поведение этих реализаций?

+1

'std :: vector' не был реализован в стандартном C++ с C++ 03. –

+1

@ T.C .: Ухаживать за этим? Учитывая это, знаешь, _is_. –

+3

Возвращаемое значение @LightnessRacesinOrbit 'data()' '(или в C++ 03, '& v [0]') требуется, чтобы разрешить арифметику указателя на нем, а арифметика указателя определена только для указателей в один и тот же массив. 'vector', по очевидным причинам, фактически не может использовать массив внутри. –

ответ

2

0 Как есть std::launder Предпочитаемый код для использования? Нужно ли начинать и заканчивать отмывание каждый раз, когда используется новое место?

От P0532R0, вы могли бы избежать необходимости вызвать launder(), если возвращаемое значение размещения нового присваивается end. Вам не нужно было бы менять указатель начала, если только вектор не был пуст, так как объект, на данный момент указываемый start, по-прежнему будет иметь активный срок службы с кодом, который вы указали.

В этой же статье указывается, что launder() является не-оп, если время жизни объекта не закончился, и был заменен на новый объект, поэтому использование launder() не повлечет за собой снижение производительности, если это не является необходимым:

[...] тип std::launder(this) эквивалентен именно этому, как указал Ричард Смит: Помните, что launder(p) - это не-op, если p не указывает на объект, срок жизни которого закончился и где новый объект был создан в том же хранилище ,

Текущие реализации stdlib, по-видимому, используют код, аналогичный приведенному выше. Не определено ли поведение этих реализаций?

Да. P0532R0 также обсуждает эту проблему, и содержимое похоже на обсуждение в комментариях вопроса: vector не использует непосредственное размещение размещения, возвращаемое значение нового вызова места теряется в цепочке вызовов функций на распределитель вектора и в любом размещение события new используется элемент за элементом, поэтому построение внутреннего векторного механизма в любом случае не может использовать возвращаемое значение. launder() представляется инструментом, предназначенным для использования здесь. Однако тип указателя, указанный распределителем, не обязательно должен быть сырым типом указателя вообще, а launder() работает только для необработанных указателей. Текущая реализация в настоящее время не определена для некоторых типов; launder(), по-видимому, не является подходящим механизмом для решения общего случая для контейнеров на основе распределителей.

Смежные вопросы