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;
Sign up to request clarification or add additional context in comments.

9 Comments

"Have to" might be bit strong; the two step approach can also work. Although the boost approach is certainly good.
Geussing you meant: #include <boost/assign/std/vector.hpp>
@Jason: boost.assign defines templated overloads of operator+= and operator, on vector<T>
but how do you intercept each of the comma values?
12345? I've got the same combination on my luggage!
|
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 Comments

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: 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.
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 Comment

wicked, but useful for testing, etc. ++
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 Comments

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.
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.
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.

Comments

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 Comment

Nevermind, "Steve Jessop" has point this approach out already.
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;
}

Comments

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.

Comments

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

Comments

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 Comments

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?
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
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. :-)
... 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.
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 ....
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.