2012-08-30 10 views
11

In C++ 11, ci sono due versioni di std::vector::resize():Dovrebbe (in C++ 11) std :: vector :: resize (size_type) funzionare per il valore predefinito constr_appy int [4]?

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

ho capito (come suggerito da uno dei commenti a una delle risposte a this question) che il primo richiede value_type essere costruibile di default, mentre il secondo richiede che sia una copia costruibile. Tuttavia, (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 

Quindi la mia comprensione era sbagliata o gcc è bacato. Quale?

+0

Utilizzare questa definizione di blocco: 'blocco struct {int arr [4]; }; ' – PiotrNycz

+0

possibile duplicato di [errore del compilatore con C++ std :: vector of array] (http://stackoverflow.com/questions/12184828/compiler-error-with-c-stdvector-of-array) –

+0

@PiotrNycz: Al contrario di 'std :: array '? – ildjarn

risposta

10

Il requisito (23.3.6.3:10) su vector.resize(n) essendo ben formata è che T dovrebbe essere CopyInsertable, vale a dire che il seguente dovrebbe essere ben formato (23.2.1: 13):

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

dove A è il tipo di allocatore del vettore, m è l'allocatore, p è di tipo T * e v è di tipo T.

Come si può scoprire da 20.6.8.2:5, questo non è valido per i tipi di array nel caso generale in quanto è equivalente alla chiamata

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

che è valida per i tipi di array (matrici non possono essere inizializzati parentesi).


In realtà, hai ragione che g ++ ha un bug; dovrebbe essere sempre possibile aggirare il problema con CopyInsertable fornendo un allocatore appropriato, ma g ++ non riesce a permettere questo:

#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 
} 

Un altro problema è nella norma stessa; 20.9.4.3:3 specifica std::is_default_constructible<T> come equivalente a std::is_constructible<T>, dove 20.9.4.3:6 specifica std::is_constructible<T, Args...> come criterio di ben formata su T t(std::declval<Args>()...), che è valido per i tipi di array (come @Johannes Schaub-litb sottolinea, i tipi di array possono essere inizializzati con (zero-pack-expansion)). Tuttavia, 17.6.3.1:2 richiede per DefaultConstructible inoltre che T() sia ben formato, il che non è il caso per un tipo di array T ma non è controllato da std::is_default_constructible.

+0

qualcuno dovrebbe presentare una segnalazione di bug qui. – Walter

+1

Prima non ho capito il tuo 'T t()'. Immagino sia importante ricordare che è il risultato di un 'T t (espansione del pacchetto zero)'. Altrimenti è davvero difficile capire cosa intendi per "contesto a definizione variabile". Ma 'T t (espansione del pacchetto zero)' * è * valido se 'T' è un tipo di array e i tipi di elementi possono essere inizializzati in valore. Solo 'T()' è mal formato se 'T' è un tipo di matrice. –

+0

@ JohannesSchaub-litb grazie, sembra che questa parte sia un bug nella definizione dello standard di 'std :: is_default_constructible'. – ecatmur

0

Ho trovato questa discussione dopo che mi sono imbattuto in un problema simile con resize() non funzionante per un tipo costruibile di default. Sembra che l'implementazione del vettore gcc non sia corretta.

FYI, ho presentato un bug contro gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64147