How to initialize std::vector from C-style array?

155,814

Solution 1

Don't forget that you can treat pointers as iterators:

w_.assign(w, w + len);

Solution 2

You use the word initialize so it's unclear if this is one-time assignment or can happen multiple times.

If you just need a one time initialization, you can put it in the constructor and use the two iterator vector constructor:

Foo::Foo(double* w, int len) : w_(w, w + len) { }

Otherwise use assign as previously suggested:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

Solution 3

Well, Pavel was close, but there's even a more simple and elegant solution to initialize a sequential container from a c style array.

In your case:

w_ (array, std::end(array))
  • array will get us a pointer to the beginning of the array (didn't catch it's name),
  • std::end(array) will get us an iterator to the end of the array.

Solution 4

You can 'learn' the size of the array automatically:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

Hopefully, you can change the interface to set_data as above. It still accepts a C-style array as its first argument. It just happens to take it by reference.


How it works

[ Update: See here for a more comprehensive discussion on learning the size ]

Here is a more general solution:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

This works because the array is being passed as a reference-to-an-array. In C/C++, you cannot pass an array as a function, instead it will decay to a pointer and you lose the size. But in C++, you can pass a reference to the array.

Passing an array by reference requires the types to match up exactly. The size of an array is part of its type. This means we can use the template parameter N to learn the size for us.

It might be even simpler to have this function which returns a vector. With appropriate compiler optimizations in effect, this should be faster than it looks.

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

Solution 5

The quick generic answer:

std::vector<double> vec(carray,carray+carray_size); 

or question specific:

std::vector<double> w_(w,w+len); 

based on above: Don't forget that you can treat pointers as iterators

Share:
155,814

Related videos on Youtube

Frank
Author by

Frank

Updated on November 26, 2020

Comments

  • Frank
    Frank over 3 years

    What is the cheapest way to initialize a std::vector from a C-style array?

    Example: In the following class, I have a vector, but due to outside restrictions, the data will be passed in as C-style array:

    class Foo {
      std::vector<double> w_;
    public:
      void set_data(double* w, int len){
       // how to cheaply initialize the std::vector?
    }
    

    Obviously, I can call w_.resize() and then loop over the elements, or call std::copy(). Are there any better methods?

    • Void
      Void over 14 years
      The crux of the problem is that there is no way for the vector to know if the same allocator was used to create your C-style array. As such the vector must allocate memory using its own allocator. Otherwise it could simply swap out the underlying array and replace it with your array.
  • Frank
    Frank over 14 years
    In my case, the assignment will happen repeatedly.
  • Frank
    Frank over 14 years
    Is there a performance difference between std::copy(w, w + len, w_.begin()) and your assign solution?
  • Frank
    Frank over 14 years
    Oh, I guess the difference is that std::copy will not resize the array.
  • Matteo Italia
    Matteo Italia over 14 years
    I don't know if assign is smart enough to compute how much w is distant from w+len (a generic iterator AFAIK does not provide a quick way to do this), so you may enhance a little the performances putting a w_.reserve(len) before that statement; in the worst case you gain nothing. In this way it should have more or less the performance of resize+copy.
  • Pavel Minaev
    Pavel Minaev over 14 years
    It's a quality of implementation issue. Since iterators have tags that specify their categories, an implementation of assign is certainly free to use them to optimize; at least in VC++, it does indeed do just that.
  • jamk
    jamk about 11 years
    The quick solution could be std::vector<double> w_(w,w+len);
  • Harini Sampath
    Harini Sampath about 10 years
    This copies elements to a newly created storage for 'w_'; 'w_.data' will not point to 'w'. You still have to deallocate 'w'. There's no ownership transfer
  • Pavel Minaev
    Pavel Minaev about 10 years
    Sure, but this is as good as you can get with a vector. It does not provide any facility to attach to an existing buffer.
  • Wayne Uroda
    Wayne Uroda about 9 years
    Is this safe to do when when w+len points to the element past the end of the array, as in, when you want to use this method to take the last 10 elements of a 100 element array? In VS2013 I got a debug assertion that the source array had been accessed out of bounds, which made me cautious, and I ended up using a more C-style approach.
  • Pavel Minaev
    Pavel Minaev about 9 years
    If it's one element past the end, it should be okay (just as v.end() is an iterator pointing one past the end with vector in a similar case). If you do get an assertion, then something is off elsewhere.
  • Vlad
    Vlad over 8 years
    What includes/version of C++ does this require?
  • Vlad
    Vlad over 8 years
    what if data to be shared between the vector and an array? Do we need to copy anything in this case?
  • Mugurel
    Mugurel over 8 years
    This is one of the constructors of std::vector from at least c++98 onwards.... It's called 'range constructor'. cplusplus.com/reference/vector/vector/vector Try it.
  • Andrew Romanov
    Andrew Romanov over 8 years
    More independent version is: w_ (std::begin(array), std::end(array)); (In the future you can to change a C array for a C++ container).
  • ShadowRanger
    ShadowRanger about 8 years
    Mind you, this only works if you have a real array (which usually means you're copying from a global or local (declared in current function) array). In the OP's case, he's receiving a pointer and a length, and because it's not templated on the length, they can't change to receiving a pointer to a sized array or anything, so std::end won't work.
  • Vineel
    Vineel almost 8 years
    Also if you want to statically allocate in C++11 use initializer_list like below vector<double> w_{1.2, 2.3, 4.3};
  • M.M
    M.M over 7 years
    vector does not overload operator(), so this won't compile. std::end being called on a pointer is no use either (the question asks to assign a vector from a pointer and a separate length variable). It would improve your answer to show more context about what you are trying to suggest
  • M.M
    M.M over 7 years
    In the last sample, return { begin(source_array), end(source_array) }; is also possible
  • Jean-François Fabre
    Jean-François Fabre almost 6 years
    is that an answer or a question? what does it bring to the already existing answers?
  • Janusz Lenar
    Janusz Lenar almost 6 years
    @Jean-FrançoisFabre and what does your comment bring? ;) true, it's a poor answer given ages ago.
  • Adrian Albert Koch
    Adrian Albert Koch over 4 years
    Just quickly, will this deallocate the array memory when the vector goes out of scope?