views:

1439

answers:

7

I am unclear about the following.

First, this code compiles fine:

#include <vector>

typedef struct{
 int x1,x2,x3,x4;
}  ints;

typedef std::vector<ints> vec;

int main(){
 vec v;
 ints a = {0,1,2,3};
 v.push_back(a);
}

The following code is near identical:

#include <vector>

typedef std::vector<int[4]> vec;

int main(){
 vec v;
 int a[4] = {0,1,2,3};
 v.push_back(a);
}

but it throws the extremely length error output I will include at the end. Why does the compiler treat these two programs so differently? It is definitely not intuitive.

Here is the compiler error that is thrown on my system that is using g++ to compile:

[mattg@pigott Test]$ g++ test2.cpp -o test2
In file included from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/i586-redhat-linux/bits/c++allocator.h:34,
                 from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/allocator.h:48,
                 from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/vector:62,
                 from test2.cpp:2:
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = int [4]]’:
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/stl_vector.h:737:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = int [4], _Alloc = std::allocator<int [4]>]’
test2.cpp:9:   instantiated from here
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/ext/new_allocator.h:105: error: ISO C++ forbids initialization in array new
In file included from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/vector:69,
                 from test2.cpp:2:
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = int [4], _Alloc = std::allocator<int [4]>]’:
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/stl_vector.h:741:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = int [4], _Alloc = std::allocator<int [4]>]’
test2.cpp:9:   instantiated from here
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/vector.tcc:306: error: array must be initialized with a brace-enclosed initializer
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/stl_vector.h:741:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = int [4], _Alloc = std::allocator<int [4]>]’
test2.cpp:9:   instantiated from here
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/vector.tcc:312: error: invalid array assignment
In file included from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/i586-redhat-linux/bits/c++allocator.h:34,
                 from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/allocator.h:48,
                 from /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/vector:62,
                 from test2.cpp:2:
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::destroy(_Tp*) [with _Tp = int [4]]’:
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/vector.tcc:353:   instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = int [4], _Alloc = std::allocator<int [4]>]’
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/stl_vector.h:741:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = int [4], _Alloc = std::allocator<int [4]>]’
test2.cpp:9:   instantiated from here
/usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/ext/new_allocator.h:115: error: request for member ‘~int [4]’ in ‘* __p’, which is of non-class type ‘int [4]’
+5  A: 

Under the hood it's doing an assignment and that isn't defined for arrays.

The pertinent part of the error is

instantiated from here /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/vector.tcc:306: error: array must be initialized with a brace-enclosed initializer /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/stl_vector.h:741: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = int [4], _Alloc = std::allocator]’ test2.cpp:9: instantiated from here /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/bits/vector.tcc:312: error: invalid array assignment

Lou Franco
The way these template errors work is that the compiler tries to instantiate the template given your type -- then it compiles the resulting code. When it runs into an error, it tries to give you an idea of how the code it found the problem in relates to the code you wrote, and it kind of assumes that you know the source-code of the template so that you can understand the error. You have to kind of ignore all of the "Instantiated from" comments to find the problem
Lou Franco
I heard C++0x (aka C++1x, at this rate) may end up with friendlier errors on this stuff.
Brian
@Brain probably not. The improved errors were contingent on Concepts, which have been removed from C++0x.
Pete Kirkham
+2  A: 

It's been a little while since I used C++, but I believe the core problem you're encountering is that arrays don't have the required semantics to get along well with a std::vector<>. I don't have my copy of Stroustrup handy, or I'd give you a reference.

Greg D
+4  A: 

Try boost::array instead of plain arrays. It provides STL-compliant interface around fixed-size arrays, so it can be used inside STL containers. Plus, it implements boundary checking (boost::array::at).

#include <boost/array.hpp>
#include <vector>

typedef std::vector< boost::array<int, 4> > vec;
int main(){
    vec v;
    boost::array<int, 4> va = {0,1,2,3};
    v.push_back(va);
}
PiotrLegnica
Alternatively, use `std::tr1::array` (which is same as Boost one, but ISO-standardized) if that's available on your implementation.
Pavel Minaev
+6  A: 

error: ISO C++ forbids initialization in array new
error: array must be initialized with a brace-enclosed initializer
error: invalid array assignment
error: request for member ‘~int [4]’ in ‘* __p’, which is of non-class type ‘int [4]’

To understand one of the errors, imagine the following:

void main() {
    int a[4] = {0,1,2,3};
    int b[4] = a;
}

As opposed to:

typedef struct{
    int x1,x2,x3,x4;
}  ints;

int main()
{
    ints a;
    ints b = a;
}

Or even:

typedef struct{
    int x[4];
}  ints;

int main()
{
    ints a;
    ints b = a;
}

C/C++ arrays cannot be copied via the assignment operator, however struct's containing arrays can be.
So an easy fix is to do:

typedef struct{
        int x[4];
}  ints;

typedef std::vector<ints> vec;

int main(){
        vec v;
        ints a = { {0,1,2,3} };
        v.push_back(a);
}
Amro
+1  A: 

Try using a vector of vector instead.

Brian
I think there might be legitimate desire to use vector of array. (I myself have probably overused vector of vector, since I wasn't aware of things like boost::array.)
UncleBens
+1  A: 

The requirement for value type T for all STL containers, including std::vector<T>, is that T is Assignable - ISO C++03 23.1[lib.container.requirements]/4-5. Assignable is defined as follows:

Expression t = u, where t is of type T, and u is of type cv T, is valid, its return type is T&, and the post-condition is that t is equivalent to u.

Arrays do not fulfill this requirement, because you cannot write:

int a[2], b[2];
a = b;

The reason why you cannot is because both a and b in the code snippet above decay to pointer-type rvalues according to the usual C++ rules for array-to-pointer decay described in 4.2[conv.array]. Naturally, an rvalue if not permitted on the left side of non-overloaded operator=.

Pavel Minaev
A: 

Arrays are not first class in C++; you cannot pass them as arguments to functions, for example (they decay to pointers, although you can pass pointers and references to arrays). Further, they do not have value semantics.

DrPizza