Должен ли (в С++ 11) std::vector::resize(size_type) работать для конструируемого по умолчанию значения_типа int[4]?

В C++11 есть две версии std::vector::resize():

void resize( size_type count );
void resize( size_type count, const value_type& value);

Я понимаю (как было предложено одним из комментариев к одному из ответов на этот вопрос), что первое требует, чтобы value_type было конструируемым по умолчанию, а второе требует, чтобы его можно было копировать. Однако (gcc 4.7.0)

using namespace std;
typedef int block[4];
vector<block> A;
static_assert(is_default_constructible<block>::value,";-("); //  does not fire
A.resize(100);                                               //  compiler error

Так что либо я неправильно понял, либо gcc глючит. Который?


person Walter    schedule 30.08.2012    source источник
comment
Используйте это определение блока: struct block { int arr[4]; };   -  person PiotrNycz    schedule 30.08.2012
comment
возможный дубликат ошибки компилятора с C++ std::vector of array   -  person Nicol Bolas    schedule 30.08.2012
comment
@PiotrNycz: В отличие от std::array<int, 4>?   -  person ildjarn    schedule 30.08.2012
comment
@ildjarn Почему против? Это более или менее то же самое... Из-за плохих условий работы я все еще на С++ 03 - поэтому я все еще думаю на С++ 03;)   -  person PiotrNycz    schedule 30.08.2012


Ответы (2)


Требование (23.3.6.3:10) к правильному формату vector.resize(n) заключается в том, что T должно быть CopyInsertable, т. е. следующее должно быть правильно сформированным (23.2.1:13):

allocator_traits<A>::construct(m, p, v);

где A — тип распределителя вектора, m — распределитель, p — тип T *, а v — тип T.

Как вы можете обнаружить из 20.6.8.2:5, это недопустимо для типов массивов в общем случае, поскольку это эквивалентно вызову

::new(static_cast<void *>(p))block(v);

что недопустимо для типов массивов (массивы не могут быть инициализированы круглыми скобками).


На самом деле, вы правы, что в g++ есть ошибка; всегда должна быть возможность обойти проблему с CopyInsertable, предоставив соответствующий распределитель, но g++ не позволяет этого:

#include <vector>

template<typename T, int n> struct ArrayAllocator: std::allocator<T[n]> {
    void construct(T (*p)[n], T (&v)[n]) {
        for (int i = 0; i < n; ++i)
            ::new(static_cast<void *>(p + i)) T{v[i]};
    }
};

int main() {
    std::vector<int[4], ArrayAllocator<int, 4>> c;
    c.resize(100);  // fails

    typedef ArrayAllocator<int, 4> A;
    A m;
    int (*p)[4] = 0, v[4];
    std::allocator_traits<A>::construct(m, p, v); // works
}

Еще одна ошибка в самом стандарте; 20.9.4.3:3 указывает std::is_default_constructible<T> как эквивалент std::is_constructible<T>, где 20.9.4.3:6 указывает std::is_constructible<T, Args...> в качестве критерия правильности для T t(std::declval<Args>()...), который действителен для типов массивов (как указывает @Johannes Schaub-litb, типы массивов могут быть инициализированы с (zero-pack-expansion)). Однако 17.6.3.1:2 требует, чтобы DefaultConstructible вдобавок T() был правильно сформирован, что не относится к типу массива T, но не проверяется std::is_default_constructible.

person ecatmur    schedule 30.08.2012
comment
кто-то должен отправить отчет об ошибке здесь. - person Walter; 30.08.2012
comment
Сначала я не понял твоего T t(). Я думаю, важно упомянуть, что это результат T t(zero pack expansion). В противном случае действительно трудно понять, что вы подразумеваете под контекстом определения переменной. Но T t(zero pack expansion) действительно допустимо, если T является типом массива и типы элементов могут быть инициализированы значением. Только T() имеет неправильный формат, если T является типом массива. - person Johannes Schaub - litb; 01.09.2012
comment
@JohannesSchaub-litb спасибо, похоже, эта часть является ошибкой в ​​стандартном определении std::is_default_constructible. - person ecatmur; 03.09.2012
comment
Я спросил Даниэля Крюглера, и он сказал, что уже работает над чем-то подобным (исправление определения DefaultConstrictible для агрегатов). Он согласен со мной, что результат черты более ожидаем и что язык, вероятно, должен быть исправлен, чтобы разрешить ArrayType() (тем более, что ArrayType{} также разрешен). Я верю в него, что он примет соответствующие меры. - person Johannes Schaub - litb; 04.09.2012

Я нашел это обсуждение после того, как столкнулся с аналогичной проблемой, когда resize() не работает для конструируемого по умолчанию типа. Кажется, что реализация вектора gcc неверна.

К вашему сведению, я сообщил об ошибке в gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64147

person Eric Friedman    schedule 02.12.2014