为什么 CRTP 模板中的派生类不初始化静态变量?

Why does a derived class from CRTP template not initialise the static variables?

提问人:ashura 提问时间:7/27/2023 最后编辑:ashura 更新时间:7/27/2023 访问量:57

问:

所以我正在尝试创建一个基于 CRTP 的工厂。为简单起见,我将只包括此处相关的任何内容。我有两个可能彼此无关的问题,但试图找到关键字时遇到了困难。

#include <bits/stdc++.h>
using namespace std;

static auto& GetFactoryMap() {
    static vector<string> GLOBAL_FACTORY_MAP;
    return GLOBAL_FACTORY_MAP;
}

template <typename Derived>
struct Registrar {
    static int DoRegister() {
        auto name = typeid(Derived).name();
        GetFactoryMap().emplace_back(name);
        return 1;
    };
    inline static const int _hello = DoRegister();
};

struct Foo : public Registrar<Foo> {};

int main() {
    cout << GetFactoryMap().size();
}

用 , 标志 编译。x86-64 clang-8.0.0-std=c++17

问题 1:程序返回:https://godbolt.org/z/5c74TePT5。我希望它打印出来,因为定义类也应该定义一个 ,这反过来将初始化 和 调用。01FooRegistrar<Foo>_helloDoRegister()

但是当我这样做时:

int main() {
    cout << Foo::_hello << endl;
    cout << GetFactoryMap().size();
}

然后它确实打印了()和(地图大小)。1_hello1

问题 2:所以我发现了如何通过在构造函数内部调用虚拟调用来解决第一个问题。_helloRegistrar

template <typename Derived>
struct Registrar {
    Registrar() {
        (void)_hello;
    }
    ...
}

但问题是,我仍然不知道为什么它可以解决这个问题。即使在我这样做之后,似乎只有当我有一个空构造函数或类定义之外的默认构造函数时才会被调用,如全局地图大小仅为 2:https://godbolt.org/z/Me53q1x86 所示。DoRegister

这是怎么回事?

模板 C++17 工厂 派生类 CRTP

评论


答:

1赞 n. m. could be an AI 7/27/2023 #1
  1. 隐式实例化

    除非模板化类的成员是声明的专用化,否则当在要求成员定义存在的上下文中引用该专用化时,或者如果成员定义的存在影响程序的语义,则隐式实例化该成员的专用化;具体而言,除非静态数据成员本身以需要静态数据成员定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何关联的副作用)。

    TL的;DR 如果未使用,则不会实例化。

  2. 显式默认函数

    未定义为删除的非用户提供的默认函数(即,在类中隐式声明或显式默认)在使用 odr ([basic.def.odr]) 或需要常量计算 ([expr.const]) 时隐式定义。

    TL的;类中的 DR 只声明一个事物;如果未使用,则未定义。=default

评论

0赞 ashura 7/27/2023
谢谢你的回答。对于我的第二个问题,为什么添加内部类定义与添加外部定义有不同的结果?你可以参考我的 godbolt 链接 godbolt.org/z/Me53q1x86Hi()=defaultHi::Hi() = default
0赞 n. m. could be an AI 7/27/2023
它就在报价中。在类中显式默认
0赞 ashura 7/27/2023
啊,我明白了,所以意思是它“不在课堂上”。好吧,再次澄清一下,是什么让定义派生类的 ctor 会将静态成员标记为“已使用”?仅仅是因为没有定义 ctor 的类被编译掉了,而有 ctor 的类将调用基 ctor(它“使用”它)?Hi::Hi() = default
0赞 n. m. could be an AI 7/27/2023
没错,如果定义派生类的 ctor,则它使用基类的 ctor(以及它调用的所有其他内容),而基类的 ctor 使用静态成员。
0赞 ashura 7/27/2023
嗯,现在我有点困惑了。什么是“使用”?在我的示例代码中,我实际上并没有显式“使用”这些类中的任何一个,我只指定了构造函数(并且没有使用它们)。为什么它们被认为是“使用”的?