6

For example

struct A
{
    static vector<int> s;
};

vector<int> A::s = {1, 2, 3};

However, my compiler doesn't support initialization list. Any way to implement it easily? Does lambda function help here?

user1899020
  • 13,167
  • 21
  • 79
  • 154

5 Answers5

4

I always fear being shot down for initialization ordering here for questions like this, but..

#include <iostream>
#include <vector>
#include <iterator>

struct A
{
    static std::vector<int> s;
};

static const int s_data[] = { 1,2,3 };
std::vector<int> A::s(std::begin(s_data), std::end(s_data));

int main()
{
    std::copy(A::s.begin(), A::s.end(), 
              std::ostream_iterator<int>(std::cout, " "));
    return 0;
}

Output

1 2 3

Just because you can doesn't mean you should =P

Winning the award for the least efficient way to do this:

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

template<typename T>
std::vector<T> v_init(const T& t)
{
    return std::vector<T>(1,t);
}

template<typename T, typename... Args>
std::vector<T> v_init(T&& t, Args&&... args)
{
    const T values[] = { t, args... };
    std::vector<T> v1(std::begin(values), std::end(values));
    return v1;
}

struct A
{
    static std::vector<int> s;
};

std::vector<int> A::s(v_init(1,2,3,4,5));


int main(int argc, const char *argv[])
{
    std::copy(A::s.begin(), A::s.end(), std::ostream_iterator<int>(std::cout, " "));
    return 0;
}

Output

1 2 3 4 5 

This should puke at compile-time if T and anything in Args... is not type-compliant or type-castable. Of course, if you have variadic templates odds are you also have initializer lists, but it makes for fun brain-food if nothing else.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
4

Any way to implement it easily?

There's nothing particularly elegant. You can either copy the data from a static array, or initialise it with the result of a function call. The former might use more memory than you'd like, and the latter needs some slightly messy code.

Boost has a library to make that slightly less ugly:

#include <boost/assign/list_of.hpp>
vector<int> A::s = boost::assign::list_of(1)(2)(3);

Does lambda function help here?

Yes, it can save you from having to name a function just to initialise the vector:

vector<int> A::s = [] {
    vector<int> v;
    v.push_back(1);
    v.push_back(2); 
    v.push_back(3);
    return v;
}();

(Strictly speaking, this should have an explicit return type, []()->vector<int>, since the lambda body contains more than just a return statement. Some compilers will accept my version, and I believe it will become standard in 2014.)

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • `boost::list_of` is wonderful. But I have to use `boost::assign::list_of` to let it work. And in fact my vector is `vector>` Is the following has no memory leak? `vector> A::s = boost::assign::list_of(new int(2));` – user1899020 Sep 17 '13 at 15:45
  • @user1899020: Sorry, I forgot the `assign` part; I haven't used that library since brace-initialisation came along. – Mike Seymour Sep 17 '13 at 15:46
  • 1
    @user1899020: The `unique_ptr` initialisation should be fine as long as it compiles and none of the allocations fail. Since this is initialising a static object, failure will end the program, so there's no particular need to worry about leaks. More generally, you should use a `make_unique` function to immediately wrap each naked pointer before anything might throw. There should be a `std::make_unique` in C++14. – Mike Seymour Sep 17 '13 at 15:49
3

Write a simple init function for the vector:

vector<int> init()
{
  vector<int> v;
  v.reserve(3);
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  return v;
};

vector<int> A::s = init();
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • This has undefined behavior all over the place (not to mention that it won't compile). – James Kanze Sep 17 '13 at 16:13
  • @James it seems I forgot two lines of code. Fixed. And optimized. – rubenvb Sep 17 '13 at 17:21
  • OK. Not the simplest nor the most optimal, but acceptable. (The simplest is to just define a static C style array, and use `begin` and `end` on it as arguments to the two iterator constructor of `vector`.) – James Kanze Sep 17 '13 at 18:20
  • @James both solutions copy elements into a runtime allocated vector. Not much we can do about that without a `constexpr`'ed `initializer_list` constructor... – rubenvb Sep 18 '13 at 08:08
1

You can initialize an std::vector from two pointers

int xv[] = {1,2,3,4,5,6,7,8,9};
std::vector<int> x(xv, xv+(sizeof(xv)/sizeof(xv[0])));

You can even factor this out in a template function:

template<typename T, int n>
std::vector<T> from_array(T (&v)[n]) {
    return std::vector<T>(v, v+n);
}
6502
  • 112,025
  • 15
  • 165
  • 265
  • Formally, your initialization expression has undefined behavior. `&xv[sizeof(xv)/sizeof(xv[0])] is an out of bounds access. Practically, it will probably work. But why all the complexity: `std::vector x( std::begin( xv ), std::end( xv ) );`. (Of course, you don't have C++11, because otherwise, you'd use a new style initializer. So in fact, you'll have to use the equivalents of `std::begin` and `std::end` from your toolkit.) – James Kanze Sep 17 '13 at 16:17
  • @Kanze: No. For statically allocated arrays it's permitted to take **the address** of the past-last-element (you cannot dereference the address, but I'm not doing that). There is (was) what I think is a "bug" in the specs because this is not guaranteed for **dynamically** allocated arrays and `(new int[10])+10` is indeed formally UB. In my case it's valid code however. – 6502 Sep 17 '13 at 16:29
  • And what does `xv[sizeof(xv)/sizeof(xv[0])]` do? It dereferences the one past the end pointer. (It is the exact equivalent, by definition, of `*(xv + sizeof(xv)/sizeof(xv[0]))`.) – James Kanze Sep 17 '13 at 17:08
  • @Kanze: No again. The expression is not `xv[sizeof...]` but `&xv[sizeof...]`. If `p` is a pointer the expression `&*p` is NOT dereferencing the pointer. For a description of why `int x[10]; &x[10];` is legal see http://stackoverflow.com/a/988220/320726 (includes citations from the standard). – 6502 Sep 17 '13 at 17:50
  • The expression `xv[sizeof...]` has undefined behavior. What you do after the undefined behavior is irrelevant. C has a special rule to allow this; the C++ committee considered adding the special rule to C++, and decided not to. The answer you quote is wrong. – James Kanze Sep 17 '13 at 18:18
  • @JamesKanze: I never thought about address-of in `&*p` happening "after" the dereference. Actually it's totally obvious that the address is something you need BEFORE you can dereference and that's why common sense would say there can be no problem in `&xv[size]` even if there can be problems in `xv[size]` (because adding `&` before implies LESS operations are done, and more specifically the memory doesn't need to be accessed). I will take your word that C++ in this area is worse than C **by design** (I tried to read the standard but I've better riddles to play with). I fixed the answer. – 6502 Sep 18 '13 at 19:24
  • Common sense doesn't always apply with regards to the standard:-). C90 (and thus C++) were carefully written to allow a bounds checking implementation, and to allow the compiler to do bounds checking on `*p`. C has since made a special exception for `&*p`. C++ didn't follow, probably because if `p` is actually a user defined type with a user defined `operator*`, there's no way to allow it. – James Kanze Sep 19 '13 at 08:16
  • And what's wrong with just: `std::vector( v, v + n )`? It's certainly more idiomatic, and probably clearer, than `&v[0]`. – James Kanze Sep 19 '13 at 08:18
  • @JamesKanze: nothing really in this case :-) . I'm just used to `&v[0]` because I used it quite often with vectors. Fixed again. – 6502 Sep 19 '13 at 10:37
  • Me too. But I'll bet you don't use `&v[v.size()]` with vectors:-). (It should crash unless you're building an optimized version.) – James Kanze Sep 19 '13 at 10:58
0

Another idea:

struct A
{
  static std::vector<int> s;
};

std::vector<int> A::s;

static bool dummy((A::s.push_back(1), A::s.push_back(2), A::s.push_back(3), false));
user1095108
  • 14,119
  • 9
  • 58
  • 116