55

在非模板库类中拥有静态成员而不将定义成员的负担放在类用户身上的最佳方法是什么?

假设我想提供这个类:

class i_want_a_static_member
{
    static expensive_resource static_resource_;

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

然后该类的用户一定不要忘记在某处定义静态成员(正如已经多次回答的 那样):

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

我在下面有一个答案,但它有一些缺点。有更好和/或更优雅的解决方案吗?

4

3 回答 3

70

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 开始,初始化需要是线程安全的,这通常意味着任何访问都由原子读取和良好预测的分支控制。

于 2012-07-29T16:53:43.607 回答
18

我自己的解决方案是使用模板化的持有者类,因为静态成员在模板中工作正常,并将这个持有者用作基类。

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

但是由于成员的名称是在 holder 类中指定的,因此您不能将同一个 holder 用于多个静态成员。

于 2012-07-29T14:06:41.800 回答
7

从 C++ 17 开始。您现在可以使用内联变量来执行此操作:

static const inline float foo = 1.25f;
于 2018-11-13T22:22:03.393 回答