Трябва ли (в C++11) std::vector::resize(size_type) да работи за конструктивния value_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 на масив   -  person Nicol Bolas    schedule 30.08.2012
comment
@PiotrNycz : За разлика от std::array<int, 4>?   -  person ildjarn    schedule 30.08.2012
comment
@ildjarn Защо против? Горе-долу е същото... Поради лошите условия на работата ми все още съм на C++03 - така че все още мисля на C++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
попитах daniel kruegler и той каза, че вече работи върху подобно нещо (коригиране на дефиницията на 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