79

Is there an elegant way to create and initialize a const std::vector<const T> like const T a[] = { ... } to a fixed (and small) number of values?
I need to call a function frequently which expects a vector<T>, but these values will never change in my case.

In principle I thought of something like

namespace {
  const std::vector<const T> v(??);
}

since v won't be used outside of this compilation unit.

vscharf
  • 893
  • 1
  • 7
  • 5

10 Answers10

64

For C++11:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };

Original answer:

You would either have to wait for C++0x or use something like Boost.Assign to do that.

e.g.:

#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope

vector<int> v;
v += 1,2,3,4,5;
Antonio
  • 19,451
  • 13
  • 99
  • 197
Ferruccio
  • 98,941
  • 38
  • 226
  • 299
39

If you're asking how to initialise a const vector so that it has interesting contents, then the answer is probably to use the copy constructor. First you laboriously fill in a vector, then you create your new const vector from it. Or you can use the vector<InputIterator>(InputIterator, InputIterator) constructor template to initialise from some other kind of container or an array. If an array, then that could have been defined with an initialisation list.

Something like this is hopefully close to what you want:

const T ra[3] = {t1, t2, t3};
const vector<const T> v(ra, ra+3);

If you're asking how to pass a const vector into a function which takes a vector then the answer is either:

  • you can't, because the function might alter the vector and your object/reference is const. Make a non-const copy of the original, and pass that in.

or

  • use const_cast to remove the constness in order to pass it into a function which takes a non-const vector but which you just so happen to know will not modify the vector.

The latter is one of those things which will, quite rightly, cause anyone who sees it to make comments about goggles, and the fact that they do nothing. It's exactly what const_cast is for, but there's a reasonably strong argument that says if you need const_cast, you have already lost.

Doing both of those things (creating a const vector from a non-const one with the copy constructor, and then casting away constness) is definitely wrong - you should have just used a non-const vector. So pick at most one of these to do...

[Edit: just noticed that you're talking about a difference between vector<T> and const vector<const T>. Unfortunately in the STL, vector<const T> and vector<T> are completely unrelated types, and the only way to convert between them is by copying. This is a difference between vectors and arrays - a T** can be silently and safely converted to const T *const *]

Waqar
  • 8,558
  • 4
  • 35
  • 43
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • If I don't want to copy t1, t2, t3, how can I do this a best? This is the only/best solution I found so far: const int *p = &ra[0]; vector v; for(int i = 0; i < size; ++i, ++p) v.push_back(p); – AudioDroid May 16 '12 at 10:38
  • @AudioDroid: yes, there's no way to put anything into a vector without copying it (or moving it, in C++11), so the closest you'll get is a vector of pointers (or smart pointers). But if you have your three objects in an array already, then normally there's no point creating a vector of pointers to them: just use the array for whatever it is you would use the vector for. – Steve Jessop May 16 '12 at 10:54
15

Short and dirty way (similar to Boost's list_of())

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(const T& t) {
        (*this)(t);
    }
    vlist_of& operator()(const T& t) {
        this->push_back(t);
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
}

Now, C++11 has initializer lists, so you don't need to do it that way or even use Boost. But, as an example, you can do the above in C++11 more efficiently like this:

    #include <iostream>
    #include <vector>
    #include <utility>
    #include <ostream>
    using namespace std;

    template <typename T>
    struct vlist_of : public vector<T> {
        vlist_of(T&& t) {
            (*this)(move(t));
        }
        vlist_of& operator()(T&& t) {
            this->push_back(move(t));
            return *this;
        }
    };

    int main() {
        const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
        for (const auto& i: v) {
            cout << i << endl;
        }
    }

But, it's still not as efficient as using a C++11 initializer list because there's no operator=(vlist_of&&) defined for vector.

tjohns20's way modified like the following might be a better c++11 vlist_of:

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

template <typename T>
class vlist_of {
    public:
        vlist_of(T&& r) {
            (*this)(move(r));
        }
        vlist_of& operator()(T&& r) {
            v.push_back(move(r));
            return *this;
        }
        vector<T>&& operator()() {
            return move(v);
        }
    private:
        vector<T> v;
    
};

int main() {
    const auto v = vlist_of<int>(1)(2)(3)(4)(5)();
    for (const auto& i : v) {
        cout << i << endl;
    }
    
}
Waqar
  • 8,558
  • 4
  • 35
  • 43
Shadow2531
  • 11,980
  • 5
  • 35
  • 48
12

As others have said, you can't init a vector the same way you can init a C-style array, unless you give it pointers to a source array. But in that case, if your vector is a global const, why not just use an old C-style array instead?

const int MyInts[] = {
1, 2, 3, 4, 5};

const size_t NumMyInts = sizeof(MyInts)/sizeof(MyInts[0]);

You can even use STL algorithms against this array, the same way you would use algorithms against a const vector...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 4
    That's a good point, but he's passing it to a function which expects a vector. He may not be able to modify that function. – Ferruccio Oct 23 '08 at 21:11
  • This requires tracking the size of the vector externally, which is a pain when dealing with many vectors. Otherwise this solution would be the best one as it only requires one copy of the integers in memory. – MasterHD Jul 14 '16 at 06:03
6

You can do it in two steps:

namespace {
    const T s_actual_array[] = { ... };
    const std::vector<const T> s_blah(s_actual_array,
        s_actual_array + (sizeof(s_actual_array) / sizeof(s_actual_array[0])));
}

Perhaps not as beautiful as you might like, but functional.

janm
  • 17,976
  • 1
  • 43
  • 61
5

How about:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);
Smi
  • 13,850
  • 9
  • 56
  • 64
opal
  • 143
  • 1
  • 6
3

Old question, but I ran into the same issue today, here's the approach that was most acceptable for my purposes:

vector<int> initVector(void)
{
    vector<int> initializer;
    initializer.push_back(10);
    initializer.push_back(13);
    initializer.push_back(3);
    return intializer;
}

int main()
{
    const vector<int> a = initVector();
    return 0;
}

Example to avoid excessive copying:

vector<int> & initVector(void)
{
    static vector<int> initializer;
    if(initializer.empty())
    {
        initializer.push_back(10);
        initializer.push_back(13);
        initializer.push_back(3);
    }
    return intializer;
}

int main()
{
    const vector<int> & a = initVector();
    return 0;
}
Kevin
  • 31
  • 2
0

Not sure if I understood you right. I understand your question like this: you want to initialize a vector to a large number of elements. What's wrong with using push_back() on the vector? :-)

If you know the number of elements to be stored (or are sure that it will store less than the next power of 2) you can do this, if you have a vector of pointers of type X (works only with pointers):

std::vector< X* > v;
v.reserve(num_elems);
X* p = v.begin();
for (int count = 0; count < num_elems; count++)
   p[count] = some_source[count];

Beware of adding more than the next power of 2 elements, even if using push_back(). Pointers to v.begin() will then be invalid.

Waqar
  • 8,558
  • 4
  • 35
  • 43
mstrobl
  • 2,381
  • 14
  • 16
  • There are many problems with this code. You can't just call vector::reserve() and then start manipulating pointers and depending on magic implementation details like powers of two. Iterator converted to pointer? What about vector::size()? On what compiler and STL library does this even compile? – janm Oct 23 '08 at 21:37
  • I agree this is very bad code. A the very best, you should forget the use of the pointer/iterator and fallback to push_back – paercebal Oct 23 '08 at 21:53
  • Nope, that's perfectly legal C++ code. The STL guarantees that this will work: vector will allocate consecutive space for at least 2^n elements. After all 2^n elements vector is allowed to realloc, giving you a new space behind vector::begin(). Sorry, it might be hacky, but it's the standard. :-) – mstrobl Oct 23 '08 at 21:57
  • I see the downvotes, but insist that there exist good reasons for using this STL property (it's there for a reason). Hey, there are a lot of doctoral theses out there that use this property :) – mstrobl Oct 23 '08 at 22:01
  • Sorry, how, exactly is: "X* p = v.begin();" standard? How is using operator[] to access out of bounds elements in a vector standard? What is special about pointers that makes this work over (say) integers? Where does this code even compile? – janm Oct 23 '08 at 22:03
  • Janm, the STL defines this behaviour as standard behaviour, like it or not. Go ahead and try on your C compiler (incl missing asterisk :). It is useful if you want the power of std::vector and need the data pointed to consecutively, such as when sending vertex data to a graphics card. – mstrobl Oct 23 '08 at 22:08
  • Just tried on VC2005 and gcc 3.4.6. Failed, first error on "X** p = v.begin();" You have to change it to "X** p = &v[0];" to even get it to compile. It is still broken; what do you expect v.size() to return? – janm Oct 23 '08 at 22:23
  • @janm: you mis-copied his code. It reads X*p=, not X**p=. @mstrobl: power-of-2 is not in the standard (at least not in 23.2.4), but using reserve to ensure begin remains valid is in the standard (23.2.4.2(5)). I'm unsure if converting vector::iterator to T* is standard, but &*v.begin() is T*. – Steve Jessop Oct 24 '08 at 09:23
  • 1
    ... however, I'm deeply unconvinced that using the contiguous memory guarantee to add new values by pointer is standard - for starters, the vector doesn't get a chance to update its own size, so I'm fairly sure the results of this code are either undefined, or a vector of size 0. – Steve Jessop Oct 24 '08 at 09:35
  • ... you can use the contiguous memory guarantee to reset existing values by pointer. 23.2.4(1) guarantees that &v[n] == &v[0]+n for 0 <= n < v.size(). It says nothing about v.size() <= n < v.capacity(). – Steve Jessop Oct 24 '08 at 09:39
  • ... so your strange choice of loop would probably be valid if you initialize with v(num_elems) (constructor (size_type)) rather than the no-args constructor followed by a call to reserve. But then you might as well just loop setting v[count] instead of messing about with p. – Steve Jessop Oct 24 '08 at 09:50
  • I've just checked with GCC, and vector::iterator isn't convertible either to int* (as mstrobl's code claims) or to int** (as janm thought he intended to say). &*v.begin() is an int** as I expected, so obviously not convertible to int* either. – Steve Jessop Oct 24 '08 at 10:03
  • Thanks for the defense, onebyone! I wrote that very late at night yesterday. The initialization was asked for by the question as I interpreted it. And it's true that this is ugly, but again, been late. The usual way would be the other way around of course, using push_back and then taking [contd] – mstrobl Oct 24 '08 at 10:03
  • [contd] advantage of the linear space - many people don't know that. Onebyone obviously does, and that's good to see. A pity that janm would want to just discard my answer. As to where the power of two comes in: http://www.sgi.com/tech/stl/FAQ.html – mstrobl Oct 24 '08 at 10:07
  • 1
    Of course you can take advantage of contiguous space in the vector; the standard guarantees that. However, calling reserve() is not sufficient to do that; you must add the elements such that size() is correct. You could change the code so that you allocated the correct number of elements .... – janm Oct 24 '08 at 11:17
  • ... in the vector constructor, for example "vector v(n)", and then use "T* p = &v[0]", that would be fine. The problems I have with this example are: (1) Calling reserve() instead of calling a function that changes the size(), (2) Attempting to cast an iterator to a pointer ... – janm Oct 24 '08 at 11:19
  • ... and (3) asserting that it only works for a container with pointers. – janm Oct 24 '08 at 11:20
  • I did indeed make a few mistakes with my implementation. As I already wrote, it was late and I was trying to answer the question that I interpreted to be asking for just that. I considered the vector to alrady have a sufficient size but did not point that out. janm's remark (3) points out a .. – mstrobl Oct 24 '08 at 12:36
  • ... mistake I made. Regarding (2), that was not my intent. "(&(T&)) std::vector::begin()" will point to the first element of the vector, which was my intent. The code might seriously lack in its implementation and I may have made errors, but it's not inherently bad and also there's no absurdely.. – mstrobl Oct 24 '08 at 12:39
  • ... hackish voodo in it. There's no "weird magic number" of 2^n as a comment, that I think I remember, which is now gone used to bemoan. So, also seeing the sparked discussion, I do not see how you can sweep away the example merely as "bad code". I still insist that the "-1" is unjustified. – mstrobl Oct 24 '08 at 12:45
  • Btw, that SGI FAQ refers only to their implementation. There may be other implementations of STL, which use a constant other than 2, so relying on it is non-standard. Also it only says it doubles capacity each time: if it starts at or is reserve()d to a non-power, then it will remain a non-power. – Steve Jessop Oct 24 '08 at 14:32
  • I'd strongly suggest that anyone who wants to write portable C++ grab a copy of the standard - it saves prefacing everything you do with "this works on GCC 4.4 for x86 linux with SGI", and wondering whether that means it will work elsewhere too :-) – Steve Jessop Oct 24 '08 at 14:37
  • Oh, and I say "a constant other than 2" because vector guarantees amortised constant-time addition, and I don't know of a way of doing that other than multiplying the capacity by some constant each time it's extended. But the constant doesn't have to be 2. – Steve Jessop Oct 24 '08 at 14:44
  • Of course the -1 is justified. The only correct statement in the response is "What's wrong with using push_back() on the vector?". Of cours the number of elements to store is known (it's a constant!) and powers of two are irrelevant (and an imp. detail). The rest of the example is just wrong. – janm Oct 26 '08 at 10:41
  • In response to my first comment where I pointed out the iterator to pointer conversion and the issue with size() and the powers of two being an implemenation detail your response was "Nope, that's perfectly legal C++ code." The fact that you can access the memory in an already allocated vector ... – janm Oct 26 '08 at 10:43
  • ... using pointers very little to do with allocating a vector from constants (ie: answering the actual question). And you're code wasn't even doing that. I think you're lucky you've only got -1. – janm Oct 26 '08 at 10:45
  • @onebyone: I did "T**" because of the comment from mstrobl: "Go ahead and try on your C compiler (incl missing asterisk :)" which was really the only way it would get close. I agree with your comments. Converting vector::iterator to T* is not standard. Pointer to a deref'd iter is different. – janm Oct 26 '08 at 10:48
0

If they're all the same you can just do

vector<T> vec(num_items, item);

but I assume they're not - in which case the neatest way is probably:

vector<T> vec(num_items);
vec[0] = 15;
vec[1] = 5;
...

C++0x will let you use an initialiser list in exactly the way you're thinking of, but that's not a lot of good right now, unfortunately.

Peter
  • 7,216
  • 2
  • 34
  • 46
0

Based on Shadow2531's response, I'm using this class to initialise vectors, without actually inheriting from std::vector like Shadow's solution did

template <typename T>
class vector_init
{
public:
    vector_init(const T& val)
    {
        vec.push_back(val);
    }
    inline vector_init& operator()(T val)
    {
        vec.push_back(val);
        return *this;
    }
    inline std::vector<T> end()
    {
        return vec;
    }
private:
    std::vector<T> vec;
};

Usage:

std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();

Compared to Steve Jessop's solution it creates a lot more code, but if the array creation isn't performance critical I find it a nice way to initialise an array in a single line

tjohns20
  • 299
  • 1
  • 3