简体   繁体   English

为什么在这种情况下没有初始化 c++ 静态成员?

[英]Why c++ static member was not initialized in this case?

Use CRTP to get new instance in c++.使用 CRTP 在 C++ 中获取新实例。 But failed.但是失败了。 Because registerT is not called and the function was not registered in the map.因为没有调用registerT并且该函数没有在映射中注册。 I have no clue.我没有任何线索。 This is the code.这是代码。

//factory.h //工厂.h

#pragma once

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <cstdlib>
#include <cxxabi.h>
template <typename BaseType, typename... Args>
class Factory {
public:
    static std::shared_ptr<BaseType> Make(const std::string &name) {
        if (constructMap().find(name) == constructMap().end()) {
            return nullptr;
        }
        std::cout << "name2:" << name  <<std::endl;;
        return constructMap().at(name)();
    }
    friend BaseType;
public:
    template <typename T>
    class Registrar : public BaseType {
        public:
            friend T;
            static bool registerT() {
                const auto name = "Derive";
                std::cout << "name;" << name <<std::endl;
                Factory::constructMap()[name] = []() -> std::shared_ptr<BaseType> {
                    return std::make_shared<T>();
                };
                return true;
            }
            static bool registered;

        private:
            Registrar() : BaseType() {
                std::cout << "registered;" << std::endl;
                 (void)registered;
            }
    };

    private:
        Factory() = default;
        static auto& constructMap() {
            static std::unordered_map<std::string, std::shared_ptr<BaseType> (*)(Args...)> funcMap;
            return funcMap;
        }

};

template <typename BaseType, typename... Args>
template <typename T>
bool Factory<BaseType, Args...>::Registrar<T>::registered =
    Factory<BaseType, Args...>::Registrar<T>::registerT();

//base.h //base.h

#pragma once
#include "factory.h"

class Base : public Factory<Base> {
public:
    Base() {}
};

//derive.h //导出.h

#pragma once
#include <string>
#include "base.h"

class Derive : public Base::Registrar<Derive> {
public:
    Derive();
};

//derive.cpp //导出.cpp

#include "derive.h"

Derive::Derive() {}

//main.cpp //main.cpp

#include "base.h"

int main() {
  std::string name = "Derive";
  auto a = Base::Make(name);
  auto b = Base::Make(name);
  return 0;
}

Use g++ -o mian ./main.cpp -Lderive.o to get exe file.使用g++ -o mian ./main.cpp -Lderive.o获取 exe 文件。 But the function registerT was not called when exec ./mian但是执行./mian时没有调用函数registerT

It works when using g++ -o mian ./main.cpp derive.o to get exe file.它在使用g++ -o mian ./main.cpp derive.o获取 exe 文件时有效。 Thank you.@user17732522谢谢。@user17732522

First of all, the compilation command is wrong.首先,编译命令错误。 The -L option expects a path to a directory containing libraries which are specified later. -L选项需要一个包含稍后指定的库的目录的路径。 I am not sure what you are attempting by using it here, but as a result you are not linking the derive translation unit.我不确定您在此处使用它正在尝试什么,但结果是您没有链接derive翻译单元。 .o files should simply be listed like source files, eg .o文件应该像源文件一样简单地列出,例如

g++ -o mian main.cpp derive.o

(This part doesn't apply anymore after the question was edited in response.) (这部分在问题被编辑后不再适用。)

After fixing that.修好之后。

The program has undefined behavior.该程序具有未定义的行为。

This is because there is no guarantee that Derive::name is initialized before Factory<Base>::Registrar<Derive>::registered .这是因为不能保证Derive::nameFactory<Base>::Registrar<Derive>::registered之前初始化。 In particular the latter is a specialization of a template and therefore has so-called unordered dynamic initialization , meaning that there is no ordering guarantee for it with any other global static storage duration object.特别是后者是模板的特化,因此具有所谓的无序动态初始化,这意味着它与任何其他全局静态存储持续时间对象都没有排序保证。

However, the initialization of the latter calls registerT() which accesses the former in const auto name = T::name;但是,后者的初始化调用registerT()访问前者在const auto name = T::name; . . If name has not been initialized at this point, accessing it causes undefined behavior.如果此时name尚未初始化,则访问它会导致未定义的行为。

The usual workaround is to not rely on inter-dependent dynamically-initialized static members, but instead use static functions with local static variables as you are already doing for constructMap .通常的解决方法是不依赖于相互依赖的动态初始化静态成员,而是使用带有局部静态变量的静态函数,就像您已经为constructMap映射所做的那样。 Eg here you could use the same for name .例如,在这里您可以使用相同的name


Additionally, even if there was no ordering issue with the static storage duration objects you defined yourself, there is still the issue that the standard streams need to be initialized before std::cout may be used.此外,即使您自己定义的静态存储持续时间对象没有排序问题,仍然存在标准流需要在使用std::cout之前初始化的问题。 They are initialized through construction of an object of type std::ios_base::Init .它们是通过构造std::ios_base::Init类型的对象来初始化的。 Including <iostream> behaves as if it defined a global static storage duration object of that type, which usually guarantees it is initialized before you use it.包含<iostream>的行为就好像它定义了该类型的全局静态存储持续时间对象,这通常保证在您使用它之前对其进行初始化。 But again, here registered is unordered with it.但同样,这里registered的是无序的。

So you must create an object of type std::ios_base::Init yourself in registerT before trying to write to std::cout .因此,在尝试写入std::cout之前,您必须自己在registerT中创建一个std::ios_base::Init类型的对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM