Как иметь статические элементы данных в библиотеке только заголовка?
-
13-12-2019 - |
Вопрос
Какой лучший способ иметь статический элемент в классах без шаблонного библиотеки, Не размещая бремя определения участника на класс пользователя?
сказать, что я хочу предоставить этот класс:
class i_want_a_static_member
{
static expensive_resource static_resource_;
public:
void foo()
{
static_resource_.bar();
}
};
.
Тогда пользователь класса не должен забывать определить статический член где-то (Как уже ответил Многие times ):
// this must be done somewhere in a translation unit
expensive_resource i_want_a_static_member::static_resource_;
.
У меня есть ответ ниже, но у него есть некоторые недостатки.Есть ли лучшие и / или более элегантные решения?
Решение
Используйте переменные генеракодицетагкода для нединамической инициализации:
struct Foo
{
inline static int I = 0;
};
.
и используйте функцию локальные статические переменные иначе:
struct Foo
{
static std::string& Bar()
{
static std::string S = compute();
return S;
}
};
.
<Сильные> C ++ 14 и ниже
Используйте функцию локальной статики, так как они проще проще в использовании.
Если по какой-то причине вы действительно хотите для статического элемента данных , то вы можете использовать трюк шаблона:
template <typename T = void>
struct Foo
{
static int I = 0; // inline initialization only for simple types.
};
template <typename T>
int Foo<T>::I;
.
на локальной статии
Для ресурсов, которые требуют динамической инициализации, лучше всего использовать локальный статический.
Порядок, в котором файл-охватывает или статистика схватки классов динамически инициализированы не определены, в целом, что приведет к статическому инициализации заказа Fiasco, когда вы пытаетесь прочитать неинициализированную статическую в рамках инициализации другого. Местный статический решают проблему, будучи инициализированным лениво, при первом использовании.
Есть несколько легких накладных расходов для использования локальной статики. От C ++ 11 инициализация требуется для безопасного потока, что обычно означает, что любой доступ устанавливается атомной прочитанной и просвещенной веткой.
Другие советы
Мое собственное решение состоит в том, чтобы использовать класс держателя для шаблонов, поскольку статические элементы работают нормально в шаблонах, и используют этот держатель в качестве базового класса.
template <typename T>
struct static_holder
{
static T static_resource_;
};
template <typename T>
T static_holder<T>::static_resource_;
.
Теперь используйте класс держателя:
class expensive_resource { /*...*/ };
class i_want_a_static_member : private static_holder<expensive_resource>
{
public:
void foo()
{
static_resource_.bar();
}
};
.
Но как имя участника указано в классе держателя, вы не можете использовать тот же держатель для более чем одного статического элемента.
как в C ++ 17. Теперь вы можете использовать встроенные переменные, чтобы сделать это:
static const inline float foo = 1.25f;
.