使用继承的成员函数指针的 unordered_map,使用 std:function

Using unordered_map of member function pointers with inheritance, using std:function

我正在尝试创建和使用 unordered_map 成员函数指针,作为接口的一部分,由子 类 继承和使用。最终的目标是让子类可以被脚本("scriptable")使用,这样他们的函数就可以被文本调用"commands."

我在语法方面遇到了问题,并且在成员函数指针及其继承和使用方面遇到了困难。我尝试了很多变化,但这是一个复杂而微妙的问题,正如我的谷歌搜索所显示的那样。

界面:

#include <string>
#include <vector>
#include <unordered_map>
#include <functional>

using namespace std;

class IScriptable
{
    public:
        virtual void Init() = 0;
        bool Run_Script_Command(string i_Command, vector<string> i_Arguments);
    protected:
        void Register_Command_Function_Pair(string i_Command, function<void(const IScriptable&, vector<string>)> i_Function);

        unordered_map<string, function<void (const IScriptable&, vector<string>)>> m_Command_Functions;
};

bool IScriptable::Run_Script_Command(string i_Command, vector<string> i_Arguments)
{
    if (m_Command_Functions.find(i_Command) != m_Command_Functions.end())
    {
        (m_Command_Functions[i_Command])(*this, i_Arguments);
    }
    else
    {
        return(false);
    }

    return(true);
}

void IScriptable::Register_Command_Function_Pair(string i_Command, function<void(const IScriptable&, vector<string>)> i_Function)
{
    m_Command_Functions[i_Command] = i_Function;
}

子对象:

class Child : public IScriptable
{
    public:
        virtual void Init();
    protected:
        void Foo(vector<string> parms); // The function I'm going to try and add and invoke later
};

void Child::Init()
{
    Register_Command_Function_Pair("TestFunction", &Child::Foo);
}

void Child::Foo(vector<string> parms)
{
    cout << "Calling Foo\n";
    for (vector<string>::iterator it = parms.begin(); it != parms.end(); it++)
    {
        cout << *it;
        cout << "\n";
    }
}

代码使用示例:

int main()
{
    Child c;
    c.Init();

    vector<string> arguments;
    arguments.push_back("Testing");
    arguments.push_back("123");
    c.Run_Script_Command("TestFunction", arguments);

    // Expected output:
    // Calling Foo
    // Testing
    // 123  
}

我已经尝试创建一个我正在尝试做的事情的简化示例,所以希望我的目标是有意义的,即使有一些其他糟糕的设计决策(为了示例而做出的)。

我怎样才能让它编译并给我预期的输出?

谢谢。

编辑:上面的代码应该抛出以下编译器错误:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(506): error C2664: 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Set(std::_Func_base<_Ret,const IScriptable &,std::vector<_Ty,std::allocator<_Ty>>> *)' : cannot convert argument 1 from '_Myimpl *' to 'std::_Func_base<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>> *'
          with
          [
              _Ret=void
  ,            _Ty=std::string
          ]
          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(442) : see reference to function template instantiation 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Do_alloc<_Myimpl,_Fret(__cdecl Child::* const &)(std::vector<_Ty,std::allocator<_Ty>>),_Alloc>(_Fty,_Alloc)' being compiled
          with
          [
              _Ret=void
  ,            _Ty=std::string
  ,            _Fret=void
  ,            _Alloc=std::allocator<std::_Func_class<void,const IScriptable &,std::vector<std::string,std::allocator<std::string>>>>
  ,            _Fty=void (__cdecl Child::* const &)(std::vector<std::string,std::allocator<std::string>>)
          ]
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(442) : see reference to function template instantiation 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Do_alloc<_Myimpl,_Fret(__cdecl Child::* const &)(std::vector<_Ty,std::allocator<_Ty>>),_Alloc>(_Fty,_Alloc)' being compiled
          with
          [
              _Ret=void
  ,            _Ty=std::string
  ,            _Fret=void
  ,            _Alloc=std::allocator<std::_Func_class<void,const IScriptable &,std::vector<std::string,std::allocator<std::string>>>>
  ,            _Fty=void (__cdecl Child::* const &)(std::vector<std::string,std::allocator<std::string>>)
          ]
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(442) : see reference to function template instantiation 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Reset_alloc<_Fret,Child,std::vector<_Ty,std::allocator<_Ty>>,std::allocator<std::_Func_class<_Ret,const IScriptable &,std::vector<_Ty,std::allocator<_Ty>>>>>(_Fret (__cdecl Child::* const )(std::vector<_Ty,std::allocator<_Ty>>),_Alloc)' being compiled
          with
          [
              _Ret=void
  ,            _Ty=std::string
  ,            _Fret=void
  ,            _Alloc=std::allocator<std::_Func_class<void,const IScriptable &,std::vector<std::string,std::allocator<std::string>>>>
          ]
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(442) : see reference to function template instantiation 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Reset_alloc<_Fret,Child,std::vector<_Ty,std::allocator<_Ty>>,std::allocator<std::_Func_class<_Ret,const IScriptable &,std::vector<_Ty,std::allocator<_Ty>>>>>(_Fret (__cdecl Child::* const )(std::vector<_Ty,std::allocator<_Ty>>),_Alloc)' being compiled
          with
          [
              _Ret=void
  ,            _Ty=std::string
  ,            _Fret=void
  ,            _Alloc=std::allocator<std::_Func_class<void,const IScriptable &,std::vector<std::string,std::allocator<std::string>>>>
          ]
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(671) : see reference to function template instantiation 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Reset<void,Child,std::vector<_Ty,std::allocator<_Ty>>>(_Fret (__cdecl Child::* const )(std::vector<_Ty,std::allocator<_Ty>>))' being compiled
          with
          [
              _Ret=void
  ,            _Ty=std::string
  ,            _Fret=void
          ]
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\functional(671) : see reference to function template instantiation 'void std::_Func_class<_Ret,const IScriptable &,std::vector<std::string,std::allocator<_Ty>>>::_Reset<void,Child,std::vector<_Ty,std::allocator<_Ty>>>(_Fret (__cdecl Child::* const )(std::vector<_Ty,std::allocator<_Ty>>))' being compiled
          with
          [
              _Ret=void
  ,            _Ty=std::string
  ,            _Fret=void
          ]
          Source\GoDatabase.cpp(3436) : see reference to function template instantiation 'std::function<void (const IScriptable &,std::vector<std::string,std::allocator<_Ty>>)>::function<void(__cdecl Child::* )(std::vector<_Ty,std::allocator<_Ty>>)>(_Fx &&)' being compiled
          with
          [
              _Ty=std::string
  ,            _Fx=void (__cdecl Child::* )(std::vector<std::string,std::allocator<std::string>>)
          ]
          Source\GoDatabase.cpp(3436) : see reference to function template instantiation 'std::function<void (const IScriptable &,std::vector<std::string,std::allocator<_Ty>>)>::function<void(__cdecl Child::* )(std::vector<_Ty,std::allocator<_Ty>>)>(_Fx &&)' being compiled
          with
          [
              _Ty=std::string
  ,            _Fx=void (__cdecl Child::* )(std::vector<std::string,std::allocator<std::string>>)
          ]

您的函数类型应该是:

std::function<void(std::vector<std::string>)>

const IScriptable&在那里不起作用,如果你想使用指向方法的指针,那将是完全不同的语法。

那么Init()可能是这样的:

using namespace std::placeholders;
Register_Command_Function_Pair("TestFunction", std::bind( &Child::Foo, this, _1 ) );

Run_Script_Command() 变为:

bool IScriptable::Run_Script_Command(string i_Command, vector<string> i_Arguments)
{
    auto f = m_Command_Functions.find(i_Command);
    if ( f != m_Command_Functions.end())
    {
        f->second(i_Arguments);
        return true;
    }
    return false;
}

请注意,您应该为正在使用的 std::function 创建类型别名(通过 typedefusing),因为您会多次使用它。这将使代码更具可读性并更容易更改该类型。