web-dev-qa-db-ja.com

ヘッダーのみのライブラリに静的データメンバーを含める方法は?

クラスユーザーにメンバーを定義する負担をかけずに、テンプレート化されていないライブラリクラスに静的メンバーを配置する最良の方法は何ですか?

このクラスを提供したいとします:

class i_want_a_static_member
{
    static expensive_resource static_resource_;

public:
    void foo()
    {
        static_resource_.bar();
    }
};

次に、クラスのユーザーは、静的メンバーをどこかに定義することを忘れてはなりません(すでに answeredmanytimes )。

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

私は以下の答えを持っていますが、いくつかの欠点があります。より優れた、またはよりエレガントなソリューションはありますか?

45
pesche

C++ 17以降

使用する inline static非動的初期化の変数:

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;

ローカル統計上

動的な初期化を必要とするリソースの場合、ローカル静的を使用するのが最善です。

ファイルスコープまたはクラススコープの静的が動的に初期化される順序は定義されていないため、一般に、初期化されていない静的を別の初期化の一部として読み取ろうとすると、静的初期化順序フィアスコになります。ローカルスタティックは、最初の使用時に遅延して初期化されることで問題を解決します。

ただし、ローカルスタティックの使用には若干のオーバーヘッドがあります。 C++ 11以降では、初期化はスレッドセーフである必要があります。これは、通常、すべてのアクセスがアトミックな読み取りと予測された分岐によってゲートされることを意味します。

47
Matthieu M.

私の独自のソリューションは、テンプレートで静的メンバーが正常に機能するため、テンプレート化されたホルダークラスを使用し、このホルダーを基本クラスとして使用することです。

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();
    }
};

ただし、メンバーの名前はホルダークラスで指定されるため、同じホルダーを複数の静的メンバーに使用することはできません。

18
pesche

C++ 17以降。インライン変数を使用してこれを実行できるようになりました。

static const inline float foo = 1.25f;
5