2

I have a list<string> myList as a private attribute of my class. The content of this list is known at compile time and is constant. Is there any compact way (in C++03) to initialize its value? I can only think of this:

MyClass::MyClass(){
myList.push_back(string("str1"));
myList.push_back(string("str2"));
myList.push_back(string("str3"));
etc.
}

I would like something like this:

MyClass::MyClass():list("str1","str2","str3",etc.){
}
Slazer
  • 4,750
  • 7
  • 33
  • 60
  • What you want to have is [std::initializer_list](http://en.cppreference.com/w/cpp/utility/initializer_list) which is only available since C++11. – leemes Nov 07 '12 at 17:26
  • You could use hacks like http://www.boost.org/doc/libs/1_52_0/libs/assign/doc/index.html :) – kennytm Nov 07 '12 at 17:29
  • You can define a global array of your strings and use list's two iterator constructor: `std::string arr[3] = { "str1", "str2", "str3" }; MyClass::MyClass() : myList(arr, arr+3) { }` – jrok Nov 07 '12 at 17:30
  • don't use `std::list` except for very specific circumstances. If you don't know what those are, don't use `std::list`. – Mooing Duck Nov 07 '12 at 18:12

2 Answers2

4

I tend do do something like this:

static const string my_strs[] =
{
  "str1",
  "str2",
  "str3"
};

static const num_my_strs = sizeof(my_strs)/sizeof(my_strs[0]);

Then when it comes time to initialize the list:

MyClass::MyClass()
:  myList(my_strs, my_strs + num_my_strs)
{
}

...or:

MyClass::MyClass()
{
  copy( my_strs, my_strs+num_my_strs, back_inserter(myList));
}

There are pros and cons to this approach. The obvious con is you now have 2 copies of your data. This also raises the question, if you know the contents at compile-time and they never change, do you even need the list at all? Why not just use the static const array?

On the pro side, the code to initialize the list is a 1-liner, and never needs to change even if you change the compile-time constant data that's being coped in to the list.

Edit: You can use standard library algorithms like find with plain old C-style arrays. For example:

const string* it = std::find(my_strs, my_strs+num_my_strs, "str1");
if( it == (my_strs+num_my_strs) ) // functionally same as 'list.end()' eg, item not found
{ 
  // not found
}
else
{
  // found -- it points to item found
  string found_item = *it;
}

Edit2 Whether or not an expression like int a[2]; &a[2]; is well-defined is being debated. I have edited the pointer math above to use a method that is well-known to not evoke UB.

Edit3 I've found the relevant passages in the Standard relating to this. The above expression (int a[2]; &a[2];) is in fact Undefined Behavior. I have already edited my answer to not use that method. Thanks to @MooingDuck for bringing this up.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Looks nice, but the need to have two copies would increase my line count exponentially. I need list, because it has iterators and I need them to call std::find(from,to,val) on my list. – Slazer Nov 07 '12 at 17:51
  • @user1459339: Are you aware that you can use the std library algorithms with the C-style array I have above? In fact, I did just that in the second example where I `copy`'ed from the array to the `list`. You could easily use `find` with my array. – John Dibling Nov 07 '12 at 18:03
  • I hesitate to downvote you, but isn't `&my_strs[num_my_strs]` UB? Since you have a raw array, why not use `myList(my_strs, my_strs+num_my_strs)`? – Mooing Duck Nov 07 '12 at 18:13
  • @JohnDibling: C++11(Jan2012) §8.3.4\6 "the subscript operator `[]` is interpreted in such a way that `E1[E2]` is identical to `*((E1)+(E2)).`" so `my_strs[num_my_strs]` _is_ dereferencing that pointer and thus is UB. But `my_strs+num_my_strs` is not UB. – Mooing Duck Nov 07 '12 at 18:32
  • @JohnDibling: I did not know I can use the algorithms with C-style array. Thanks. +1. Please, tell me what does UB mean?:) – Slazer Nov 07 '12 at 18:38
  • @user1459339 [What exactly do IB and UB mean?](http://stackoverflow.com/questions/2766731/what-exactly-do-ib-and-ub-mean) – Mooing Duck Nov 07 '12 at 18:40
  • @JohnDibling: [According to the Lounge chat](http://chat.stackoverflow.com/transcript/message/6137261#6137261) it's well formed in C, but undefined in C++. – Mooing Duck Nov 07 '12 at 19:29
  • @MooingDuck: I've found the relevant listings in the Standard, and I'm now convinced that `int a[2]; &a[2];` is indeed Undefined Behavior. – John Dibling Nov 07 '12 at 20:20
3

This code is not fully tested, but I would suggest this if you can't use the new and cool stuff in C++11:

class MyClassCreator
{
    MyClass result;
public:
    MyClassCreator(std::string param)
    {
        result.myList.push_back(param);
    }

    MyClassCreator& operator()(std::string param)
    {
        result.myList.push_back(param); 
        return *this;
    }

    operator MyClass() { return result; }

};

Usage:

MyClass c = MyClassCreator("object1")("object2")("object3");

EDIT

See full example at http://ideone.com/LWGRCc

Bob Fincheimer
  • 17,978
  • 1
  • 29
  • 54
  • 2
    Cute and clever, for which I +1, but I don't much care for this and would shoot any programmer that did this in production code. Its a little too clever to be maintainable. – John Dibling Nov 07 '12 at 17:33
  • @JohnDibling boost has this in their library. It can be optimized very well, especially in C++11 (which is funny because then you have a better way through initializer lists). Although it is a sort of "factory" method, it is a little clever to be used in production code. – Bob Fincheimer Nov 07 '12 at 17:35