Hoe statische gegevensleden in een bibliotheek met alleen kopteksten te hebben?

Wat is de beste manier om een ​​statisch lid in een bibliotheekklasse zonder sjabloon te hebben,
zonder de last van het definiëren van het lid op de klasgebruiker te leggen?

Stel dat ik deze les wil geven:

class i_want_a_static_member
{
    static expensive_resource static_resource_;
public:
    void foo()
    {
        static_resource_.bar();
    }
};

Dan mag de gebruiker van de klasse niet vergeten het statische lid ergens te definiëren
(zoals al beantwoordveeltijden):

// this must be done somewhere in a translation unit
expensive_resource i_want_a_static_member::static_resource_;

Ik heb hieronder wel een antwoord, maar het heeft enkele nadelen. Zijn er betere en/of elegantere oplossingen?


Antwoord 1, autoriteit 100%

C++17 en hoger

Gebruik inline staticvariabelen voor niet-dynamische initialisatie:

struct Foo
{
    inline static int I = 0;
};

En gebruik anders functie lokale statische variabelen:

struct Foo
{
    static std::string& Bar()
    {
        static std::string S = compute();
        return S;
    }
};

C++14 en lager

Gebruik functie lokale statische gegevens, omdat deze eenvoudig te gebruiken zijn.

Als je om de een of andere reden echt een statisch gegevenslidwenst, dan kun je de sjabloontruc gebruiken:

template <typename T = void>
struct Foo
{
     static int I = 0; // inline initialization only for simple types.
};
template <typename T>
int Foo<T>::I;

Op lokale statistieken

Voor bronnen die dynamische initialisatie vereisen, kunt u het beste een lokale static gebruiken.

De volgorde waarin de statica van het bestandsbereik of het klassebereik dynamisch wordt geïnitialiseerd, is over het algemeen niet gedefinieerd, wat leidt tot het statische initialisatievolgorde-fiasco wanneer u een niet-geïnitialiseerde static probeert te lezen als onderdeel van de initialisatie van een andere. Lokale statische gegevens lossen het probleem op door lui te worden geïnitialiseerd bij het eerste gebruik.

Er is echter enige overhead bij het gebruik van lokale statica. Vanaf C++11 moet de initialisatie thread-safe zijn, wat doorgaans betekent dat elke toegang wordt afgesloten door een atomaire lees- en goed voorspelde branch.


Antwoord 2, autoriteit 26%

Mijn eigen oplossing is om een ​​houderklasse met een sjabloon te gebruiken, aangezien statische leden prima werken in sjablonen, en deze houder als een basisklasse te gebruiken.

template <typename T>
struct static_holder
{
    static T static_resource_;
};
template <typename T>
T static_holder<T>::static_resource_;

Gebruik nu de houderklasse:

class expensive_resource { /*...*/ };
class i_want_a_static_member : private static_holder<expensive_resource>
{
public:
    void foo()
    {
        static_resource_.bar();
    }
};

Maar aangezien de naam van het lid is gespecificeerd in de houderklasse, kunt u dezelfde houder niet voor meer dan één statisch lid gebruiken.


Antwoord 3, autoriteit 10%

Vanaf C++ 17. Je kunt nu inline variabelen gebruiken om dit te doen:

static const inline float foo = 1.25f;

Other episodes