C++ 如何使用CRTP自注册类实例?

C++ 如何使用CRTP自注册类实例?,c++,c++11,visual-studio-2017,template-meta-programming,crtp,C++,C++11,Visual Studio 2017,Template Meta Programming,Crtp,我有一个与特定的CommandId关联的lambda函数注册表,其中注册的函数应该创建命令执行器类的具体实例,并向其提供一些数据: 命令ID.h 我认为访问base的静态注册表adder成员会迫使编译器提供一个实例化,但事实并非如此 下面是对Command1和Command2 <h3>Command1.h</h3> #include "CommandBase.h" class Command1 : public CommandBase<Command1, Com

我有一个与特定的
CommandId
关联的lambda函数注册表,其中注册的函数应该创建命令执行器类的具体实例,并向其提供一些数据:

命令ID.h 我认为访问base的
静态注册表adder
成员会迫使编译器提供一个实例化,但事实并非如此

下面是对
Command1
Command2

<h3>Command1.h</h3>

#include "CommandBase.h"

class Command1 : public CommandBase<Command1, CommandId::Command1>
{
public:
    typedef CommandBase<Command1, CommandId::Command1> BaseClass;
    friend class BaseClass;

public:
    Command1(CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command1() override {}

private:
    Command1() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};
在中,构造函数将强制实例化
基类::registryAdder
静态
基类成员。但显然不是,编译器只是将其剥离:

#include <iostream>
#include "Command1.h"
#include "Command2.h"
#include "Registry.h"

int main()
{
    std::cout << "There are " << Registry::GetCommands().size() << " registered commands." << std::endl;
    return 0;
}

现在我的问题是:

如何强制编译器从
CommandBase
模板类实例化那些
static
基类成员?


我当然做了一些研究,但这些问答并没有真正让我满意:

  • 。。。也许更多

我决定重新打开这个问题,并根据答案进行自我回答。似乎是一个更常见的问题(参见示例)

雅克的例子解决了这个问题:

#include <cstdint>

enum class CommandId : uint16_t {
    Command1 ,
    Command2 ,
};


#include <map>
#include <functional>
#include <string>

//#include "CommandId.h"

class Registry
{
public:
    static std::map<CommandId,std::function<void (std::string)>>& 
    GetCommands() {
        static std::map < CommandId, std::function<void(std::string)>> 
        theFunctionRegistry;
        return theFunctionRegistry;
    }
};

// #include "CommandId.h"
// #include "Registry.h"

// The general command execution interface
struct ICommand {
    virtual void Execute(const std::string& data) = 0;
    virtual ~ICommand() {}
};

template<typename Derived, CommandId CmdId>
class CommandBase : public ICommand
{
public:
    virtual ~CommandBase() {}
    void Register() {}
protected:
    // Dummy function to avoid abstract instantiations, should probably throw
    void Execute(const std::string& data) override {
    }

    // Protected constuctor meant for concrete command classes
    CommandBase(int& derivedRef) : derivedRef_(&derivedRef) {}

    // The static member that should perform the registation automagically
    static CommandBase<Derived, CmdId> registryAdder_;

    // This constructor is meant to be accessed from the above static member
    // instantiation only, and just register the lambda function 
    CommandBase() : derivedRef_(nullptr) {
        // Register a lambda function that ususe a concrete Derived instance
        Registry::GetCommands()[CmdId] = [](const std::string& data) {
            Derived derivedInstance;

            CommandBase<Derived, CmdId>* der = static_cast<CommandBase<Derived, CmdId>*>(&derivedInstance);
            // Manipulate the derived instances data and execute
            *(der->derivedRef_) = 42;
            der->Execute(data);
        };
    }

    // Provides access to the derived class instances data members and allows manipulation 
    int* derivedRef_;
};

template<typename Derived, CommandId CmdId>
CommandBase<Derived, CmdId> CommandBase<Derived, CmdId>::registryAdder_;

// #include "CommandBase.h"

class Command1 : public CommandBase<Command1, CommandId::Command1>
{
public:
    typedef CommandBase<Command1, CommandId::Command1> BaseClass;
    friend class CommandBase<Command1, CommandId::Command1>;

public:
    Command1(CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command1() override {}

private:
    Command1() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};

// #include "CommandBase.h"

class Command2 : public CommandBase<Command2, CommandId::Command2>
{
public:
    typedef CommandBase<Command2, CommandId::Command2> BaseClass;
    friend class CommandBase<Command2, CommandId::Command2>;

public:
    Command2(CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command2() override {}

private:
    Command2() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};

#include <iostream>
//#include "Command1.h"
//#include "Command2.h"
//#include "Registry.h"

int main()
{
    std::cout << "There are " << Registry::GetCommands().size() << " registered commands." << std::endl;
    return 0;
}
#包括
枚举类CommandId:uint16\t{
命令1,
命令2,
};
#包括
#包括
#包括
//#包括“CommandId.h”
类注册表
{
公众:
静态标准::映射和
GetCommands(){
静态std::map
职能登记处;
返回函数注册表;
}
};
//#包括“CommandId.h”
//#包括“Registry.h”
//通用命令执行界面
结构ICommand{
虚空执行(const std::string&data)=0;
虚拟~ICommand(){}
};
模板
类CommandBase:公共ICommand
{
公众:
虚拟~CommandBase(){}
无效寄存器(){}
受保护的:
//为了避免抽象实例化,伪函数可能会抛出
void Execute(const std::string&data)重写{
}
//用于具体命令类的受保护构造函数
CommandBase(int&derivedRef):derivedRef(&derivedRef){}
//应自动执行注册的静态成员
静态命令基寄存器加法器;
//此构造函数将从上述静态成员访问
//仅实例化,只需注册lambda函数
CommandBase():derivedRef_389;(nullptr){
//注册使用具体派生实例的lambda函数
注册表::GetCommands()[CmdId]=[](常量std::字符串和数据){
派生实例;
CommandBase*der=static_cast(&derivedInstance);
//操作派生实例数据并执行
*(der->derivedRef)=42;
命令->执行(数据);
};
}
//提供对派生类实例数据成员的访问并允许操作
int*derivedRef;
};
模板
CommandBase CommandBase::registryAdder;
//#包括“CommandBase.h”
类Command1:公共CommandBase
{
公众:
typedef命令基类;
好友类命令库;
公众:
Command1(CommandId){
基类::registryAdder_uz.Register();
}
virtual~Command1()重写{}
私人:
Command1():基类(成员)
{
基类::registryAdder_uz.Register();
}
void Execute(const std::string&data)重写{
}
私人:
国际会员组织;
};
//#包括“CommandBase.h”
类Command2:公共CommandBase
{
公众:
typedef命令基类;
好友类命令库;
公众:
命令2(命令ID){
基类::registryAdder_uz.Register();
}
virtual~Command2()重写{}
私人:
Command2():基类(成员)
{
基类::registryAdder_uz.Register();
}
void Execute(const std::string&data)重写{
}
私人:
国际会员组织;
};
#包括
//#包括“Command1.h”
//#包括“Command2.h”
//#包括“Registry.h”
int main()
{

std::我可以通过GitHub gist或其他渠道按需共享完整的VS2017项目吗?为什么每次有人调用
GetCommands
,你都会返回一份空的静态映射副本?这有什么用?我想这会导致你每次问都说有0个命令,这可以解释你的代码。@yack THX指出,当我从头开始重新创建MCVE时,这只是一个打字错误。发布实际的s,而不是看起来有点像的随机代码。请生成一个指向在线编译器的链接,该编译器编译您的代码并生成您的症状,因为您看起来像MCVE的代码显然不是您实际运行和检查它的证据。@πάνταῥεῖ 您似乎很确定,但是类
Command2
中的行
Command12CommandId){
和虚拟析构函数
~Command1()
却有所不同。
<h3>Command1.h</h3>

#include "CommandBase.h"

class Command1 : public CommandBase<Command1, CommandId::Command1>
{
public:
    typedef CommandBase<Command1, CommandId::Command1> BaseClass;
    friend class BaseClass;

public:
    Command1(CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command1() override {}

private:
    Command1() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};
#include "CommandBase.h"

class Command2 : public CommandBase<Command2, CommandId::Command2>
{
public:
    typedef CommandBase<Command2, CommandId::Command2> BaseClass;
    friend class BaseClass;

public:
    Command12CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command1() override {}

private:
    Command2() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};
BaseClass::registryAdder_.Register();
#include <iostream>
#include "Command1.h"
#include "Command2.h"
#include "Registry.h"

int main()
{
    std::cout << "There are " << Registry::GetCommands().size() << " registered commands." << std::endl;
    return 0;
}
There are 0 registered commands.
#include <cstdint>

enum class CommandId : uint16_t {
    Command1 ,
    Command2 ,
};


#include <map>
#include <functional>
#include <string>

//#include "CommandId.h"

class Registry
{
public:
    static std::map<CommandId,std::function<void (std::string)>>& 
    GetCommands() {
        static std::map < CommandId, std::function<void(std::string)>> 
        theFunctionRegistry;
        return theFunctionRegistry;
    }
};

// #include "CommandId.h"
// #include "Registry.h"

// The general command execution interface
struct ICommand {
    virtual void Execute(const std::string& data) = 0;
    virtual ~ICommand() {}
};

template<typename Derived, CommandId CmdId>
class CommandBase : public ICommand
{
public:
    virtual ~CommandBase() {}
    void Register() {}
protected:
    // Dummy function to avoid abstract instantiations, should probably throw
    void Execute(const std::string& data) override {
    }

    // Protected constuctor meant for concrete command classes
    CommandBase(int& derivedRef) : derivedRef_(&derivedRef) {}

    // The static member that should perform the registation automagically
    static CommandBase<Derived, CmdId> registryAdder_;

    // This constructor is meant to be accessed from the above static member
    // instantiation only, and just register the lambda function 
    CommandBase() : derivedRef_(nullptr) {
        // Register a lambda function that ususe a concrete Derived instance
        Registry::GetCommands()[CmdId] = [](const std::string& data) {
            Derived derivedInstance;

            CommandBase<Derived, CmdId>* der = static_cast<CommandBase<Derived, CmdId>*>(&derivedInstance);
            // Manipulate the derived instances data and execute
            *(der->derivedRef_) = 42;
            der->Execute(data);
        };
    }

    // Provides access to the derived class instances data members and allows manipulation 
    int* derivedRef_;
};

template<typename Derived, CommandId CmdId>
CommandBase<Derived, CmdId> CommandBase<Derived, CmdId>::registryAdder_;

// #include "CommandBase.h"

class Command1 : public CommandBase<Command1, CommandId::Command1>
{
public:
    typedef CommandBase<Command1, CommandId::Command1> BaseClass;
    friend class CommandBase<Command1, CommandId::Command1>;

public:
    Command1(CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command1() override {}

private:
    Command1() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};

// #include "CommandBase.h"

class Command2 : public CommandBase<Command2, CommandId::Command2>
{
public:
    typedef CommandBase<Command2, CommandId::Command2> BaseClass;
    friend class CommandBase<Command2, CommandId::Command2>;

public:
    Command2(CommandId) {
        BaseClass::registryAdder_.Register();
    }
    virtual ~Command2() override {}

private:
    Command2() : BaseClass(member_)
    {
        BaseClass::registryAdder_.Register();
    }

    void Execute(const std::string& data) override {

    }
private:
    int member_;
};

#include <iostream>
//#include "Command1.h"
//#include "Command2.h"
//#include "Registry.h"

int main()
{
    std::cout << "There are " << Registry::GetCommands().size() << " registered commands." << std::endl;
    return 0;
}