C ++静态成员初始化(内部模板有趣) [英] C++ Static member initialization (template fun inside)

查看:80
本文介绍了C ++静态成员初始化(内部模板有趣)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于静态成员初始化,我使用了一个嵌套的帮助器结构,该结构对于非模板化类很有效。
但是,如果封闭类是由模板参数化的,则如果未在主代码中访问辅助对象,则不会实例化嵌套的初始化类。
作为说明,是一个简化的示例(在我的情况下,我需要初始化一个向量)。

For static member initialization I use a nested helper struct, which works fine for non templated classes. However, if the enclosing class is parameterized by a template, the nested initialization class is not instantiated, if the helper object is not accessed in the main code. For illustration, a simplified example (In my case, I need to initialize a vector).

#include <string>
#include <iostream>

struct A
{
    struct InitHelper
    {
        InitHelper()
        {
            A::mA = "Hello, I'm A.";
        }
    };
    static std::string mA;
    static InitHelper mInit;

    static const std::string& getA(){ return mA; }
};
std::string A::mA;
A::InitHelper A::mInit;


template<class T>
struct B
{
    struct InitHelper
    {
        InitHelper()
        {
            B<T>::mB = "Hello, I'm B."; // [3]
        }
    };
    static std::string mB;
    static InitHelper mInit;

    static const std::string& getB() { return mB; }
    static InitHelper& getHelper(){ return mInit; }
};
template<class T>
std::string B<T>::mB; //[4]
template<class T>
typename B<T>::InitHelper B<T>::mInit;


int main(int argc, char* argv[])
{
    std::cout << "A = " << A::getA() << std::endl;

//    std::cout << "B = " << B<int>::getB() << std::endl; // [1]
//    B<int>::getHelper();    // [2]
}

使用g ++ 4.4.1:

With g++ 4.4.1:


  • [1]和[2]评论:

  • [1] and [2] commented:

A = Hello, I'm A.

按预期工作

[1]未注释:

A = Hello, I'm A.
B = 

我希望InitHelper初始化mB

I would expect, that the InitHelper initializes mB

A = Hello, I'm A.
B = Hello, I'm B.



按预期工作

  • [ 1]评论,[2]未评论:

    Segfault在[3]的静态初始化阶段

  • 因此我的问题:这是编译器错误还是监视器和椅子之间的错误?
    如果是后者,是否存在一种优雅的解决方案(即,无需显式调用静态初始化方法)?

    Thus my question: Is this a compiler bug or is the bug sitting between the monitor and the chair? And if the latter is the case: Is there an elegant solution (i.e. without explicitly calling a static initialization method)?

    更新I:

    这似乎是一种理想的行为(在ISO / IEC C ++ 2003标准14.7.1中定义):

    Update I:
    This seems to be a desired behavior (as defined in the ISO/IEC C++ 2003 standard, 14.7.1):


    除非类模板或成员模板的成员已显式实例化或显式特殊化,否则在需要成员定义存在的上下文中引用特殊化时,成员的特殊化将隐式实例化。特别是,除非静态数据成员本身的使用要求静态数据成员的定义存在,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。

    Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.


    推荐答案

    前一段时间在usenet上讨论了这个问题,而我试图回答关于stackoverflow的另一个问题:静态数据成员的实例化点。我认为值得减少测试用例,并单独考虑每种情况,因此让我们首先更笼统地进行研究:

    This was discussed on usenet some time ago, while i was trying to answer another question on stackoverflow: Point of Instantiation of Static Data Members. I think it's worth reducing the test-case, and considering each scenario in isolation, so let's look at it more general first:

    struct C { C(int n) { printf("%d\n", n); } };
    
    template<int N>
    struct A {
      static C c;
    }; 
    
    template<int N>
    C A<N>::c(N); 
    
    A<1> a; // implicit instantiation of A<1> and 2
    A<2> b;
    

    您已定义了静态数据成员模板。由于 14.7.1 ,此操作尚未创建任何数据成员:

    You have the definition of a static data member template. This does not yet create any data members, because of 14.7.1:


    ...特别是,除非以某种方式要求使用静态数据成员的定义才能使用静态数据成员,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。 / p>

    "... in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist."

    根据定义该词的一个定义规则,在使用该实体时需要定义某物(=实体) (在 3.2 / 2 中)。特别是,如果所有引用均来自未实例化的模板,模板的成员或 sizeof 表达式或类似的东西,它们都不使用实体(因为它们不是可能会对其进行评估,或者它们只是作为自身使用的函数/成员函数而尚不存在),因此不会实例化此类静态数据成员。

    The definition of something (= entity) is needed when that entity is "used", according to the one definition rule which defines that word (at 3.2/2). In particular, if all references are from uninstantiated templates, members of a template or a sizeof expressions or similar things that don't "use" the entity (since they are either not potentially evaluating it, or they just don't exist yet as functions/member functions that are itself used), such a static data member is not instantiated.

    隐式实例化由 14.7.1 / 7 实例化静态数据成员的声明-也就是说,它将实例化处理该声明所需的任何模板。但是,它不会实例化定义-也就是说,不会初始化初始化器,并且不会隐式定义该静态数据成员类型的构造函数(标记为已使用)。

    An implicit instantiation by 14.7.1/7 instantiates declarations of static data members - that is to say, it will instantiate any template needed to process that declaration. It won't, however, instantiate definitions - that is to say, initializers are not instantiated and constructors of the type of that static data member are not implicitly defined (marked as used).

    这都意味着,上面的代码将不会输出任何内容。现在,让我们隐式实例化静态数据成员。

    That all means, the above code will output nothing yet. Let's cause implicit instantiations of the static data members now.

    int main() { 
      A<1>::c; // reference them
      A<2>::c; 
    }
    

    这将导致两个静态数据成员存在,但问题是-初始化的顺序如何?简单看一下,可能会认为 3.6.2 / 1 适用,(我强调):

    This will cause the two static data members to exist, but the question is - how is the order of initialization? On a simple read, one might think that 3.6.2/1 applies, which says (emphasis by me):

    具有静态存储持续时间的对象在相同的翻译单元中在名称空间范围中定义,并应按其定义在翻译单元中出现的顺序进行初始化。

    "Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit."

    现在在usenet帖子中说,并在此缺陷报告中,这些静态数据成员未在转换单元中定义,但在中实例化了实例化单元,如 2.1 / 1 所述:

    Now as said in the usenet post and explained in this defect report, these static data members are not defined in a translation unit, but they are instantiated in a instantiation unit, as explained at 2.1/1:


    每个检查翻译的翻译单元以产生所需实例的列表。 [注意:这可能包括已明确请求的实例化(14.7.2)。 ]找到了所需模板的定义。由实现定义,是否要求包含这些定义的翻译单元的源是否可用。 [注:一个实现可以将足够的信息编码到翻译的翻译单元中,以确保此处不需要源。 ]执行所有必需的实例化以生成实例化单元。 [注意:它们与翻译后的翻译单元相似,但是不包含对未实例化模板的引用,也没有模板定义。 ]如果任何实例化失败,则程序格式错误。

    Each translated translation unit is examined to produce a list of required instantiations. [Note: this may include instantiations which have been explicitly requested (14.7.2). ] The definitions of the required templates are located. It is implementation-defined whether the source of the translation units containing these definitions is required to be available. [Note: an implementation could encode sufficient information into the translated translation unit so as to ensure the source is not required here. ] All the required instantiations are performed to produce instantiation units. [Note: these are similar to translated translation units, but contain no references to uninstantiated templates and no template definitions. ] The program is ill-formed if any instantiation fails.

    此类成员的实例化点也并不重要,因为这样的实例化点是实例化及其转换单元之间的上下文链接-它定义了可见的声明(如在 14.6.4.1 中指定的声明,并且每个这些点实例化必须赋予实例化相同的含义,如一个定义规则在 3.2 / 5 (最后一项)中所指定。

    The Point of Instantiation of such a member also does not really matter, because such a point of instantiation is the context link between an instantiation and its translation units - it defines the declarations that are visible (as specified at 14.6.4.1, and each of those point of instantiations must give instantiations the same meaning, as specified in the one definition rule at 3.2/5, last bullet).

    如果我们要进行有序的初始化,则必须进行安排,以免混淆实例化,而是使用显式声明-这是显式专业化的领域,因为它们是与普通声明没有什么不同。实际上,C ++ 0x将其 3.6.2 的措词更改为以下内容:

    If we want ordered initialization, we have to arrange so we don't mess with instantiations, but with explicit declarations - this is the area of explicit specializations, as these are not really different to normal declarations. In fact, C++0x changed its wording of 3.6.2 to the following:


    具有静态存储持续时间的非本地对象的动态初始化是有序的或无序的。
    显式专门的类模板静态数据成员的定义已命令初始化。其他
    类模板静态数据成员(即,隐式或显式实例化的特殊化)具有无序初始化。

    Dynamic initialization of a non-local object with static storage duration is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization.






    这对您的代码意味着


    This means to your code, that:


    • [1] [2] 评论:不存在对静态数据成员的引用,因此它们的定义(和也不声明它们,因为没有实例化 B< int> )。没有副作用。

    • [1] 未注释: B< int> ; :: getB(),它本身使用 B< int> :: mB ,这要求静态成员存在。字符串在main之前进行初始化(无论如何,在该语句之前进行初始化,作为初始化非本地对象的一部分)。什么都没有使用 B< int> :: mInit ,所以它没有被实例化,因此也没有 B< int> :: InitHelper 曾经被创建过,这使得它的构造函数不被使用,反过来,它也永远不会为<$​​ c $ c> B< int> :: mB 分配任何内容:您只会输出一个空串。

    • [1] [2] 注释:这对您有用,很幸运(或者相反:)。如上所述,不需要特定顺序的初始化调用。它可能在VC ++上运行,在GCC上失败,在clang上运行。我们不知道。

    • [1] 评论, [2] :未解决的问题:同样的问题-再次,两个静态数据成员都被使用 B< ; int> :: mInit B< int> :: getHelper B< int>的实例使用; :: mInit 将实例化其构造函数,该构造函数将使用 B< int> :: mB -但是对于您的编译器,该顺序在此特定运行中是不同的(未指定行为在不同运行之间不必保持一致):它首先初始化 B< int> :: mInit ,它将在not -尚未构建的字符串对象。

    • [1] and [2] commented: No reference to the static data members exist, so their definitions (and also not their declarations, since there is no need for instantiation of B<int>) are not instantiated. No side effect occurs.
    • [1] uncommented: B<int>::getB() is used, which in itself uses B<int>::mB, which requires that static member to exist. The string is initialized prior to main (at any case before that statement, as part of initializing non-local objects). Nothing uses B<int>::mInit, so it's not instantiated, and so no object of B<int>::InitHelper is ever created, which makes its constructor not being used, which in turn will never assign something to B<int>::mB: You will just output an empty string.
    • [1] and [2] uncommented: That this worked for you is luck (or the opposite :)). There is no requirement for a particular order of initialization calls, as explained above. It might work on VC++, fail on GCC and work on clang. We don't know.
    • [1] commented, [2] uncommented: Same problem - again, both static data members are used: B<int>::mInit is used by B<int>::getHelper, and the instantiation of B<int>::mInit will cause its constructor to be instantiated, which will use B<int>::mB - but for your compiler, the order is different in this particular run (unspecified behavior is not required to be consistent among different runs): It initializes B<int>::mInit first, which will operate on a not-yet-constructed string object.

    这篇关于C ++静态成员初始化(内部模板有趣)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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