【问题标题】:When using CRTP how do I declare variable type X in the code below使用 CRTP 时如何在下面的代码中声明变量类型 X
【发布时间】:2021-09-01 15:33:55
【问题描述】:

当使用 CRTP 时,我试图根据一些配置值创建对象。 但是我不确定调用Factory create方法时我在main方法中声明的类型应该是什么。

这是 CRTP 的基类

    template <typename TConfigurationStore>
    class ConfigManager
    {
        public:
        ConfigManager() = default;

        bool Configure(const INIReader& iniReader)
        {
            LOG(INFO) << "ConfigManager::Configure called.";
            return static_cast<TConfigurationStore*>(this)->Configure(iniReader);
        }            
    };

我们想使用的实现。

    class RedisConfiguration : public ConfigManager<RedisConfiguration>
    {
        public:
        bool Configure(const INIReader& iniReader)
        {
            LOG(INFO) << "RedisConfiguration::Configure called";
            return true;
        }   
    };

另一个实现...

    class FileConfiguration : public ConfigManager<FileConfiguration>
    {
        public:
        bool Configure(const INIReader& iniReader)
        {
            LOG(INFO) << "EmptyConfiguration::Configure called";
            return false;
        }   
    };
 class ConfigManagerFactory
    {
        public:
        template <typename TConfigurationStore>
        static ConfigManager<TConfigurationStore> *CreateConfigManager(const INIReader& iniReader)
        {
            std::string remoteConfigType = iniReader.Get("RemoteConfiguration", "type", "");
            if (remoteConfigType == REDIS_TYPE_NAME)
            {
                return new RedisConfiguration();
            }


            return new EmptyConfiguration();;
        }

        
        private:
       
        inline static const std::string REDIS_TYPE_NAME {"redis"};
        
    };

我的问题是我应该用什么代替 X

int main()
{

X myconfmgr = ConfigManagerFactory::CreateConfigManager(iniReader);
}

【问题讨论】:

  • 为什么不使用auto
  • 我需要在另一个实际使用这个对象的类的构造函数中传递这个 myconfmgr。我应该在另一个类的构造函数中使用什么类型。
  • 我假设这是因为它是一个精简的示例,但这对于 CRTP 模式甚至没有意义。派生类的 Configure 函数会影响 CRTP 基类的 Configure ——所以你永远不会调用基类,除非你只持有基类对它的引用(此时,缺少virtual析构函数如果被基删除,将正式成为UB)
  • @Human-Compiler 所以在这种情况下我无法使用 CRTP 吗?而动态多态性将是虚拟方法的唯一解决方案?
  • 我的意思是这取决于您还希望完成什么; CRTP 仍然有帮助。但是,当前的代码 sn-ps 看起来是在尝试避免 virtual 函数,同时仍然创建多态行为——这可能不像您期望的那样工作。如果您调用像ConfigManager&lt;T&gt; 这样没有virtual 析构函数的基类的析构函数(例如通过delete 基类),那么它是未定义的行为——即使派生类没有成员。它可能工作,但依赖它并不安全。

标签: c++ crtp


【解决方案1】:

如果CreateConfigManager() 的调用者没有像CreateConfigManager() 那样分析iniReader,它根本无法知道要为CreateConfigManager() 指定哪个模板参数,例如:

std::string remoteConfigType = iniReader.Get("RemoteConfiguration", "type", "");
if (remoteConfigType == "redis")
{
    auto myconfmgr = ConfigManagerFactory::CreateConfigManager<RedisConfiguration>(iniReader);
    myconfmgr->Configure(iniReader);
    ...
    delete myconfmgr;
}
else
{
    ...
}

调用者不必关心传递给工厂的类型。这首先破坏了使用工厂的全部意义。

我同意 cmets 中的@Human-Compiler。 CRTP 对于这段代码真的没有意义。简单的多态性就足够了。

去掉模板,使Configure()~ConfigManager()成为ConfigManager中的virtual,并覆盖RedisConfigurationFileConfiguration等中的Configure()。然后CreateConfigManager()可以返回一个普通的ConfigManager*(或更好,std::unique_ptr&lt;ConfigManager&gt;)。

试试这个:

class ConfigManager
{
public:
    ConfigManager() = default;
    virtual ~ConfigManager() = default;

    virtual bool Configure(const INIReader& iniReader) = 0;
};

class RedisConfiguration : public ConfigManager
{
public:
    bool Configure(const INIReader& iniReader) override
    {
        LOG(INFO) << "RedisConfiguration::Configure called";
        return true;
    }   
};

class FileConfiguration : public ConfigManager
{
public:
    bool Configure(const INIReader& iniReader) override
    {
        LOG(INFO) << "EmptyConfiguration::Configure called";
        return false;
    }   
};

class ConfigManagerFactory
{
public:
    static std::unique_ptr<ConfigManager> CreateConfigManager(const INIReader& iniReader)
    {
        std::string remoteConfigType = iniReader.Get("RemoteConfiguration", "type", "");
        if (remoteConfigType == REDIS_TYPE_NAME)
        {
            return std::make_unique<RedisConfiguration>();
        }

        ...

        return std::make_unique<EmptyConfiguration>();
    }

private:
    inline static const std::string REDIS_TYPE_NAME {"redis"};        
};
int main()
{
    auto myconfmgr = ConfigManagerFactory::CreateConfigManager(iniReader);
    // this probably should be called inside of CreateConfigManager() instead...
    myconfmgr->Configure(iniReader);
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 2012-09-05
    • 1970-01-01
    • 1970-01-01
    • 2015-04-28
    • 1970-01-01
    • 2010-09-28
    相关资源
    最近更新 更多