80

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.

10 Answers 10

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;
9
  • 1
    "Have to" might be bit strong; the two step approach can also work. Although the boost approach is certainly good.
    – janm
    Commented Oct 23, 2008 at 21:32
  • Geussing you meant: #include <boost/assign/std/vector.hpp>
    – Zac
    Commented Jun 2, 2010 at 17:12
  • 1
    @Jason: boost.assign defines templated overloads of operator+= and operator, on vector<T>
    – Ferruccio
    Commented Jan 31, 2011 at 15:27
  • but how do you intercept each of the comma values?
    – Jason S
    Commented Jan 31, 2011 at 15:37
  • 42
    12345? I've got the same combination on my luggage!
    – JRG
    Commented Aug 5, 2011 at 22:31
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 *]

2
  • 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: <code>const int *p = &ra[0]; vector<const int *> v; for(int i = 0; i < size; ++i, ++p) v.push_back(p);</code>
    – AudioDroid
    Commented May 16, 2012 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. Commented May 16, 2012 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;
    }
    
}
1
  • wicked, but useful for testing, etc. ++ Commented May 8, 2010 at 11:40
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);
2
  • 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
    Commented Oct 23, 2008 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
    Commented Jul 14, 2016 at 6: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.

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);
1
  • Nevermind, "Steve Jessop" has point this approach out already.
    – opal
    Commented May 4, 2012 at 9:43
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;
}
0
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.

0
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

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.

27
  • 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
    Commented Oct 23, 2008 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
    Commented Oct 23, 2008 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
    Commented Oct 23, 2008 at 21:57
  • 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. Commented Oct 24, 2008 at 9:35
  • 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
    Commented Oct 24, 2008 at 11:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.