Заполнение статического контейнера-члена в С++

У меня есть статический член класса, который представляет собой некоторый контейнер, например

(foo.h)

class Foo
{
   ...
private:
   static list<string> s_List;
}

Мне нужно заполнить список несколькими конкретными значениями. На самом деле это должно быть также const, но это может еще больше усложнить проблему. Все функции класса являются статическими, поэтому инициализация в конструкторе не имеет смысла.

Ответ 1

общее решение состоит в том, чтобы сделать что-то вроде этого:

// header
class Foo
{
...
private:
   static list<string> s_List;
}

// cpp
list<string> init()
{
     list<string> tmp;
     ... fill tmp with strings

     return tmp;
 }

 list<string> Foo::s_List(init());

другой метод похож на предложенный Нилом Баттерворт.

Ответ 2

Другой альтернативой является создание простого класса инициализации:

list <string> Foo::s_List;

struct Init {
   Init() {
      Foo::s_List.insert("apple");
      Foo::s_List.insert("bannana");
      Foo::s_List.insert("grapes");
   }
};

static Init doInit;

Обратите внимание, что, поскольку список является закрытым, это, вероятно, потребует от вас инициировать друга Foo. Также часто бывает удобно, чтобы такие классы содержались в классе, который они инициализируют.

Однако я просто перечитываю ваш вопрос, и возникает другая мысль - если список const, вы, вероятно, не будете его менять, и в этом случае простой массив строк, инициализированный строками в отсортированном порядке, может быть лучшее решение. Конечно, быстрее искать (используя std:: binary_search), чем список, и, конечно же, легко инициализировать.

Ответ 3

Если ваш компилятор поддерживает С++ 0x, это фактически тривиально для выполнения.

#include <iostream>
#include <list>

class Foo
{
public:
   static std::list<std::string> s_List;
};

std::list<std::string> Foo::s_List = {"hello", "world", "asdf", "qwerty"};

int main()
{
    for(const std::string& str : Foo::s_List)
        std::cout << str << std::endl;

    return 0;
}

Это работает как для константных, так и для нестационарных статических членов. Я тестировал этот фрагмент с clang-4.2, gcc-4.7, gcc-4.6 и gcc-4.5. Gcc-4.5 не поддерживает обновленный синтаксис для, поэтому вам придется использовать традиционный цикл для с итераторами. Кроме того, не забудьте передать в компилятор флаг -std = С++ 0x. Я уверенно уверен, что Visual Studio поддерживает это, но я не знаю точно и не знаю, какие версии.

Ответ 4

Это зависит от того, какие значения нужно внести в этот список. Статичны они или требуют какой-либо формы вычислений?

Если они статичны, вы можете сделать это:

namespace {
   const char* const initVals[] = { "A", "B", "C" };
}

list<string> Foo::s_list(initVals, initVals + 3);

Ответ 5

Пути, которые я (автор вопроса) тщетно пытались сделать это.


Я попытался сделать что-то вроде (в Foo.cpp):

list<string> Foo::s_List = list<string>();
Foo::s_List.insert("apple");
Foo::s_List.insert("bannana");
Foo::s_List.insert("grapes");

Но это дает ошибку компилятора.


Затем я подумал о том, чтобы сделать метод Initialize() и вызвать его прямо из кода

void Foo::Initialize()
{
    s_List.insert("rats");
    s_List.insert("cats");
}

Foo::Initialize(); 

//ошибка: компилятор считает, что это переопределение метода, а не вызов.


Единственная жизнеспособная идея, оставшаяся (еще не проверенная), должна была проверить, пуст ли пул в каждом методе, который его использует, и если это так, вызовите Initialize(). Но это уродливо!

Ответ 6

одним из возможных решений было бы использовать метод доступа, который проверяет, инициализирован ли он, и делает это, если это не так.