静态模板数据成员存储 [英] Static template data members storage

查看:126
本文介绍了静态模板数据成员存储的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我将写出正确解决问题的示例。



首先,我将声明模板用于创建单例对象已创建):
singleton_base.h

  template< class Derived> 
class SingletonBase
{
public:
static Derived * instance(){assert(s_instance); return dynamic_cast< Derived *>(s_instance); }
protected:
SingletonBase(){assert(s_instance == 0); s_instance = this; }
virtual〜SingletonBase(){assert(s_instance); s_instance = 0; }
private:
static SingletonBase * s_instance;
};

template< class Derived>
SingletonBase< Derived> * SingletonBase< Derived> :: s_instance = 0;



现在我们可以声明从模板派生的任何类,每个派生类应该有自己的s_instance。例如:



child1.h

  class Child1:public SingletonBase< Child1> 
{
...
void doSomething();
static void staticInvokeOne();
};

child2.h

  class Child2:public SingletonBase< Child2> 
{
...
void doSomethingElse();
static void staticInvokeBoth();
};

我也在 child1.cpp child2 .cpp

  void Child1 :: staticInvokeOne()
{
instance() - > doSomething
}

child2.cpp
$ b

  void Child2 :: staticInvokeBoth()
{
Child1 :: instance() - > doSomething
instance() - > doSomethingElse();
}

现在我有 Child1 Child2 有自己的 s_instance ,他们将指向该类在给定时刻的唯一实例。 p>

问题是关于此静态数据成员 s_instance 的存储。与常规静态数据成员不同,我没有指定应该在哪里分配。我当然喜欢 SingletonBase< Child1> :: s_instance SingletonBase< Child2> :: s_instance 分别在 child1.o child2.o 中,但是我可以期待或强制执行吗?



如果我将 Child1 Child2 放入两个不同的库 - lib1 lib2 。里面 Child2 :: staticInvokeBoth()有一个对 Child1 :: instance()的调用。据我所知,默认gcc的行为是在每个编译单元中生成 SingletonBase< Child1> :: s_instance 的副本,因此一个副本将在 lib2



是否还会在 lib2 SingletonBase< Child1> :: s_instance 的副本>?当然, SingletonBase< Child1> :: s_instance 的一个副本应该位于 lib1 中。如果这两个库以后在一个应用程序中一起使用,我可以确定只有一个实例 SingletonBase< Child1> :: s_instance ,并且 Child1 :: staticInvokeOne() Child2 :: staticInvokeBoth()正在使用吗?


$ b $



预先感谢您!

解决方案

这个答案与任何其他基于模板或内联函数相同 - 唯一的区别是在这种情况下变量被标记



在大多数编译器中,编译器将在每个编译单元中实例化所有需要的模板函数和静态成员变量在中。编译器还会将这些标记为弱符号;这意味着在最后的链接阶段,链接器将任意选择一个发出的副本以进入最终的可执行文件。



但是请注意,弱符号整合过程通常在静态连接阶段完成。动态链接器不会为您执行此操作。因此,应避免在共享库中具有可变(读写)模板静态数据成员。还要避免在共享库(包括RTTI数据)上的只读模板静态数据成员上的地址比较。



请记住,共享库中几乎任何更改跨越ABI边界的模板定义将打破你的ABI - 所以最好避免模板完全在共享库API中!


First I'll write example to properly address the question.

First of all, I'll declare template to be used to create singleton object (not auto-created): singleton_base.h

template <class Derived>
class SingletonBase
{
  public:
    static Derived* instance() { assert(s_instance); return dynamic_cast<Derived*>(s_instance); }
  protected:
    SingletonBase() { assert(s_instance==0); s_instance=this; }
    virtual ~SingletonBase() { assert(s_instance); s_instance=0; }
  private:
    static SingletonBase* s_instance;
};

template <class Derived>
SingletonBase<Derived>* SingletonBase<Derived>::s_instance = 0;

Now we can declare any class that derived from template, and each derived class should have its own s_instance. For example:

child1.h

class Child1 : public SingletonBase<Child1>
{
  ...
  void doSomething();
  static void staticInvokeOne();
};

child2.h

class Child2 : public SingletonBase<Child2>
{
  ...
  void doSomethingElse();
  static void staticInvokeBoth();
};

I also have Child's implementation in child1.cpp and child2.cpp respectively.

child1.cpp

void Child1::staticInvokeOne()
{
  instance()->doSomething();
}

child2.cpp

void Child2::staticInvokeBoth()
{
  Child1::instance()->doSomething();
  instance()->doSomethingElse();
}

Now I have Child1 and Child2 having their own s_instance and they will point to the only instance of that class at given moment.

The question is about storage for this static data member s_instance. Unlike with regular static data members I have not specified where it should be allocated. I would, of course, like to have SingletonBase<Child1>::s_instance and SingletonBase<Child2>::s_instance in child1.o and child2.o respectively, but is that something I can expect or enforce?

The issue gets more complicated if I put Child1 and Child2 into two distinct libraries - lib1 and lib2. Inside Child2::staticInvokeBoth() there is a call to Child1::instance(). As far as I understand, default gcc's behaviour is to generate a copy of SingletonBase<Child1>::s_instance in each compilation unit, thus one copy will be emitted in lib2.

Will it also generate a copy of SingletonBase<Child1>::s_instance in lib2? Definitely, one copy of SingletonBase<Child1>::s_instance should be in lib1. If those two libraries are later used together in one application, can I be sure that there is only one instance of SingletonBase<Child1>::s_instance and that both Child1::staticInvokeOne() and Child2::staticInvokeBoth() are using it?

Is it generally safe to use this approach with statics wrapped in template, or are there any drawbacks?

Thank you in advance!

解决方案

The answer to this is the same as with any other template-based or inline function - the only difference is in this case the variable ends up being marked for placement in a read-write section.

In most compilers, the compiler will instantiate any needed template functions and static member variables in every compilation unit they are referenced in. The compiler will also mark these as 'weak symbols'; this means that, at the final link phase, the linker will choose one of the emitted copies arbitrarily to go into the final executable.

Note, however, that the weak symbol consolidation process is usually done at the static linking stage. The dynamic linker will not do this for you. As such, you should avoid having mutable (read-write) template static data members in shared libraries. Also avoid address comparisons on read-only template static data members across shared libraries (including RTTI data).

And keep in mind in general with shared libraries that almost any change in template definitions that cross the ABI boundrary will break your ABI - so it's probably best to avoid templates entirely in shared library APIs!

这篇关于静态模板数据成员存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆