【问题标题】:How to force a static member to be initialized?如何强制初始化静态成员?
【发布时间】:2011-09-19 05:50:31
【问题描述】:

考虑这个示例代码:

template<class D>
char register_(){
    return D::get_dummy(); // static function
}

template<class D>
struct Foo{
    static char const dummy;
};

template<class D>
char const Foo<D>::dummy = register_<D>();

struct Bar
    : Foo<Bar>
{
    static char const get_dummy() { return 42; }
};

(Also on Ideone.)

我希望dummy 会在Foo 的具体实例化后立即被初始化,我在Bar 中拥有该实例化。 This question(以及最后的标准报价)解释得很清楚,为什么没有发生。

[...] 特别是,静态数据成员的初始化(以及任何相关的副作用)不会发生,除非静态数据成员本身以需要定义静态数据成员的方式使用存在。

有什么方法可以强制初始化dummy(有效地调用register_没有BarFoo的任何实例(没有实例,所以没有构造函数技巧)并且没有Foo 的用户需要以某种方式显式声明成员?不需要派生类做任何事情的额外 cookie。


编辑Found a way,对派生类的影响最小:

struct Bar
    : Foo<Bar>
{   //                              vvvvvvvvvvvv
    static char const get_dummy() { (void)dummy; return 42; }
};

不过,我仍然希望派生类不必这样做。 :|

【问题讨论】:

  • @Bo:当然可以,但是我想对派生类/外部世界隐藏这种用法,并且宁愿以某种方式将其放入Foo 本身。 ://
  • @Serge:为什么不应该呢? get_dummy 中的 dummy 是未初始化的,当然,但这并不重要。 :) 我实际上并没有使用它。
  • @Xeo:“get_dummy 中的 dummy 是未初始化的,当然,但这没关系。:) 我实际上并没有使用它。”是的。但是为什么它应该强制Foo&lt;Bar&gt;::dummy 被初始化呢?还有什么应该阻止编译器优化语句(void)dummy;(没有效果)。
  • @Serge:它强制dummy 被初始化,因为它被使用,正如引用的那样。 w.r.t.优化,我得检查一下。
  • @Xeo:“它强制 dummy 被初始化,因为它已被使用”实际上并非如此。仅当您在某处致电get_dummy() 时才使用它。静态成员和全局变量在main 的第一个语句之前或在第一次使用与该静态成员或全局变量在同一翻译单元中定义的任何对象或函数之前进行初始化。因此,如果您不使用来自实例化模板 Foo 特化的同一翻译单元的任何对象或函数,则完全允许不初始化 Foo::dummy。

标签: c++ templates static-members static-initialization


【解决方案1】:

考虑:

template<typename T, T> struct value { };

template<typename T>
struct HasStatics {
  static int a; // we force this to be initialized
  typedef value<int&, a> value_user;
};

template<typename T>
int HasStatics<T>::a = /* whatever side-effect you want */ 0;

也可以不引入任何成员:

template<typename T, T> struct var { enum { value }; };
typedef char user;

template<typename T>
struct HasStatics {
  static int a; // we force this to be initialized
  static int b; // and this

  // hope you like the syntax!
  user :var<int&, a>::value,
       :var<int&, b>::value;
};

template<typename T>
int HasStatics<T>::a = /* whatever side-effect you want */ 0;

template<typename T>
int HasStatics<T>::b = /* whatever side-effect you want */ 0;

【讨论】:

  • 第一个似乎工作on GCC,但在MSVC10 中不好,没​​有初始化发生。 :/ 第二个适用于neither GCC 和 MSVC10。 :( 编辑:哦,等等,启用了 C++0x it works on GCC。MSVC 仍然没有运气...
  • @Xeo 它在 gcc4.6 上没有 c++0x 对我有用。除了clang,没有任何其他编译器可以尝试。在铿锵声中,第二个也不起作用。公关正在进行中。
  • 天哪! user :var&lt;int&amp;, a&gt;::value 是什么意思?
  • @pure:这是一个未命名的位域,实际上是 char : 0;
【解决方案2】:

我们可以使用一个基于必须用类实例化的声明的简单技巧:

template<…>
struct Auto {
  static Foo foo;
  static_assert(&foo);
};
template<…> Foo Auto::foo=…;

请注意,一些编译器会警告与 null 的比较;如果需要,可以使用&amp;foo==&amp;foo(bool)&amp;foo((void)&amp;foo,true) 来避免这种情况。

还要注意 GCC 9.0–9.2 don’t count this as an odr-use

【讨论】:

  • 迄今为止最优雅的选择,谢谢。我有点担心我称之为“优雅”,但是嘿,这是 C++ :)
【解决方案3】:

我想到了类似的东西:

// in some c++ file (to make i with internal linkage)
static int i = init_dummy(Foo<int>::dummy);

init_dummy 的定义如下:

int init_dummy(...)
{
  return 1;
}

由于可变参数,您可以在那里放置更多初始化,例如:

static int i = init_dummy(Foo<int>::dummy, Foo<double>::dummy, Foo<whatever>::dummy);

【讨论】:

    【解决方案4】:

    我最近碰到了这个。官方的解决方案是显式模板实例化,描述为here

    在上述情况下,语句应如下所示:

    template class Foo::Bar;
    

    【讨论】:

      【解决方案5】:

      您如何检查 Bar 设置的值。 我更改了您的代码并在 bar 中添加了另一个函数:

      ....
      static char const get_dummy(int){return Foo<Bar>::dummy;}
      ....
      

      它给了我完全预期的结果。我可能没有正确理解,你到底想达到什么目的?

      静态成员在对象之间共享,因此必须在访问时解析它们的范围。这就是为什么我们使用 :: 通过明确告诉编译器这是我们要访问的类的成员。

      【讨论】:

        【解决方案6】:

        有没有办法在没有任何 Bar 或 Foo 实例的情况下强制 dummy 被初始化(有效地调用 register_)(没有实例,所以没有构造函数诡计)?

        这还不够吗?

        std::cout << Foo<int>::dummy;
        

        【讨论】:

        • 我希望除了让用户明确声明成员之外,还有其他方法。 :|
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-13
        • 1970-01-01
        • 2011-07-18
        • 2015-05-18
        • 1970-01-01
        • 2023-03-18
        相关资源
        最近更新 更多