std :: vector ‹› производное с resize (), которое не инициализирует примитивы и пересылает construct_back

Так что есть масса случаев, когда это необходимо в приложениях, чувствительных к производительности, и я, наконец, нахожусь на соломинке, которая сломала верблюдов. Его необходимо компилировать на C ++ 98, поскольку по крайней мере одна из наших платформ гарантирует соответствие только C ++ 98.

Отредактировано, надеюсь, для более ясного понимания того, что я хочу.

Пример:

// This will allocate storage for 1024, and then loop 1024 times touching all of it just to place a 0 in it 
std::vector< char > buffer( 1024 );
// Now read will write into that buffer, overwriting the 0s ( we payed for the fill unnecessarily ) 
buffer.resize( read( someSource, &buffer[0], buffer.size() ) );

Это общий интерфейс C, используемый почти всеми библиотеками C для записи данных в буфер. Те же проблемы возникают при работе с буферами, содержащими примитивы в целом. Новое изменение размера вместо этого будет выглядеть примерно так:

// Disabled for anything which doesn't pass boost::is_POD< T >, they get the standard version
void resize( size_t a_NewSize )
{
     reserve( a_NewSize );
     _end = _begin + a_NewSize;
}

construct_back будет конструктором пересылки, для 1 аргумента const он будет выглядеть примерно так (непроверено):

template< typename T1 >
void construct_back( const T1& a_Arg1 )
{
    if( capacity() <= size() ) // No room
         reserve( size() + 1 );
    // Construct in place using Ts constructor that accepts const T1&
    new (&(*end()) T( T1 );
    ++_end; // Account for new element
}

construct_back должен иметь все возможное количество аргументов ^ 2 перегрузок, это общий подход грубой силы для идеальной пересылки в C ++ 98.


person Ylisar    schedule 07.03.2012    source источник
comment
это самый простой способ спросить?   -  person Rohit Vipin Mathews    schedule 07.03.2012
comment
@Ylisar Я слышал, дружище, кажется, никто не понял твоего вопроса. Я ссылаюсь на это stackoverflow.com/questions/1461276/, чтобы, надеюсь, сделать его более понятным для других.   -  person deceleratedcaviar    schedule 09.03.2012


Ответы (3)


Чтобы предварительно выделить память в векторе, используйте vector::reserve. Это будет работать не только для примитивных типов, но и для всех типов.

Для создания элементов на месте вам нужно будет использовать C ++ 11 с vector::emplace_back или один из контейнеров, предоставленных Boost.Containers.

Если вам нужна ленивая конструкция (потому что ваши конструкторы дорогие) std::vector<boost::optional> - хорошее решение.

person pmr    schedule 07.03.2012
comment
vector :: reserve preallocates, но нет возможности использовать эту память без предварительного увеличения размера вектора, увеличение размера означает инициализацию всех элементов до размера, что, по моему опыту, в 99% случаев вы не хотите делать с примитивами. emplace_back выглядит интересно, но, к сожалению, я не могу использовать то, чего нет в C ++ 98, забыл об этом упомянуть. - person Ylisar; 07.03.2012
comment
@Ylisar Вы должны упомянуть об этом в своем вопросе. Это существенное требование ... Мне кажется, что вы на самом деле ищете не вектор, а map<std::size_t, T>. Кроме того, указанное вами поведение на самом деле недостижимо для чего-либо с нетривиальным конструктором копирования, что делает все это намного сложнее. - person pmr; 07.03.2012
comment
Я постараюсь перефразировать, поскольку я, очевидно, не очень ясно выразился. Для пересылки construct_back будет использоваться размещение new, и его довольно легко реализовать, если я что-то не пропустил (хотя и громоздко в C ++ 98, поскольку необходимо обеспечить все возможные перегрузки, обычные ограничения пересылки в C ++ 98). Я хочу, чтобы вариант resize () делал то же самое, что и резерв, но в то же время также изменял размер (это, конечно, даст нам зрелые возможности для всей адской потери, если T не является примитивом, но это можно решить с помощью disable_if). - person Ylisar; 07.03.2012
comment
@Ylisar Тогда вы получите контейнер, который ведет себя иначе, если is_pod<T> истинно. Не знаю, действительно ли это того стоит. Добавлю более стандартное решение для ленивого строительства. Кроме того, resize на vector<T>, где T является примитивом, просто закончится как memset. Это действительно далеко не дорого. - person pmr; 07.03.2012
comment
Да, возможно, это должно быть новое имя функции, которое будет более наглядным и не конфликтует со старым кодом. Даже если бы это было сокращено до memset (что не всегда возможно для всех Ts, особенно для более широких типов). memset все равно придется без необходимости касаться всего буфера, а затем read снова коснется его, начиная с самого начала. Для буфера 1024 * 32 тривиальный тест в ~ 6 раз быстрее без инициализации, для буфера 1024 * 32 он примерно в 160 раз быстрее без инициализации. boost :: optional - хороший компромисс для std :: vector ‹›, но вводит косвенное обращение. - person Ylisar; 07.03.2012

Ваш компилятор может быть достаточно умен, чтобы избежать инициализации. (Если он встраивает все, и вы используете этот вектор только локально, нетрудно сделать вывод, что инициализация устарела.)

Иначе:

Если вы так сильно заботитесь о производительности и у вас все равно есть буфер фиксированного размера, почему бы просто не использовать массив в стеке? То есть:

char buffer[1024];
size_t blen = read( someSource, buffer, sizeof(buffer) );

Вы также избегаете косвенного обращения к куче, введенного std::vector в этом случае.

Вы также можете создать свой собственный шаблонный контейнер вокруг этого, то есть:

template<typename T = char, size_t MaxSize = 1024>
struct Buffer {
    static const size_t maxsize = MaxSize;
    typedef T type;
    type data[maxsize];
    size_t len;
    Buffer() : len(0) {}
};

И вставьте туда любые другие функции, как хотите (чтобы он был похож на STL-контейнер).

person Albert    schedule 09.03.2012
comment
Использование буфера в стеке часто бывает приятным, но у него есть некоторые недостатки. В идеале я хотел бы более или менее заменить std :: vector ‹›. - person Ylisar; 09.03.2012

v1.reserve( 200 );

использование резерва может уменьшить количество изменений размера. Подробнее о резерве см. MSDN

person Rohit Vipin Mathews    schedule 07.03.2012