【问题标题】:How to store methods as function pointers in a map container?如何将方法存储为地图容器中的函数指针?
【发布时间】:2019-04-19 14:01:36
【问题描述】:

我希望能够根据从文件中读取的数据调用函数。 因此,对于每种项目类型,我想调用所需的读取器方法。 我写了这段代码,但它没有编译我想向地图添加函数指针的地方。怎么了?

#include <vector>
#include <map>
#include <iostream>
class reader
{

  std::map< std::string, void(*)()> functionCallMap; // function pointer
  void readA(){ std::cout << "reading A\n";};
  void readB(){ std::cout << "reading B\n";};;
public:
  reader()
  {
    *functionCallMap["A"] = &reader::readA;*
    *functionCallMap["B"] = &reader::readB;*
  }

  void read()
  {
   auto (*f) = functionCallMap["A"];
   (*f)();
  }



};

我正在 Constructor 填充地图。

【问题讨论】:

  • 指向非成员函数的指针与指向成员函数的指针不同。最大的区别是成员函数需要调用对象。您可以改用std::functionlambda expressionsstd::bind 来解决它。

标签: c++ function pointers


【解决方案1】:

您可以将 std::function 与 lambda 或 std::bind 一起使用:

class reader
{
    std::map<std::string, std::function<void()>> functionCallMap;

    void readA() { std::cout << "reading A\n"; };
    void readB() { std::cout << "reading B\n"; };

public:
    reader()
    {
        functionCallMap["A"] = [this]() { readA(); };
        functionCallMap["B"] = std::bind(&reader::readB, this);
    }

    void read()
    {
        functionCallMap["A"]();
        functionCallMap["B"]();
    }
};

【讨论】:

  • 我更喜欢您的解决方案,因为它比原始函数指针更 C++ 且更简洁。无论如何,lamba 应该优先于 std::bind (Scott Meyer's Effective Modern C++)
  • 是的,这是一个不错的解决方案。但是,根据 Jason Turner 的说法,bind 在编译时间和内存使用方面都很昂贵。
  • @RingZero 您仍应使用 std::function 而不是函数指针,我强烈建议您将标记的答案切换到此答案以供将来的读者使用。由于多种原因,使用实际的函数指针不好,但一个问题是您无法绑定到任何东西that is a lambda pointer with variable capture。您会注意到 std::function 不需要使用上述签名中的类指针。
【解决方案2】:

你需要使用指向成员函数的指针,像这样:

class reader
{
    using FuncPtr = void(reader::*)(); // function pointer
    std::map< std::string, FuncPtr> functionCallMap;
    void readA(){ std::cout << "reading A\n"; }
    void readB(){ std::cout << "reading B\n"; }
public:
    reader()
    {
        functionCallMap["A"] = &reader::readA;
        functionCallMap["B"] = &reader::readB;
    }

    void read()
    {
        auto f = functionCallMap["A"];
        (this->*f)();
    }
};

int main()
{
    reader r;
    r.read();
}

【讨论】:

    【解决方案3】:

    目前有两个答案,thisthis

    明显的区别是一个使用std::function而另一个使用函数指针。这不是重要的区别!!

    关键是成员函数是非静态成员函数。因此,它们不是类型为void()

    它们的类型为void(reader::*)()。因此,只有在给定类型为 reader 的对象时才能调用它们;人们可以将其理解为一个隐藏参数。

    first answer 只是通过指定 正确 类型来解决问题。这可以使用函数指针(如图所示)使用std::function(后者更昂贵!)来完成。

    second answer 通过绑定 指向类的特定实例的函数指针解决了这个问题。绑定后,类型确实是void()。这不能使用原始函数指针来完成(因为它们只能指向一个函数,而不是一个对象/函数对!)。

    【讨论】:

    • 另请注意,绑定的时间是设计选择。 IE。应用程序是否总是希望绑定存储函数指针的对象或可能是另一个对象?
    【解决方案4】:

    我最终得到了这个解决方案。它可以完成这项工作,但我对它的美学有些怀疑。无论如何,总结一下,我最终得到了这段代码:

    #include <map>
    #include <iostream>
    #include <functional>
    class reader
    {
        std::map< std::string, std::function<void(std::string tableName)>> functionCallMap; // function pointer
        void readA(const std::string tableName){ std::cout << "reading:" << tableName<< "\n"; }
        void readB(const std::string tableName){ std::cout << "reading:" << tableName <<"\n"; }
    public:
        reader()
        {
          functionCallMap["A"] = std::bind(&reader::readA, this, std::placeholders::_1);
          functionCallMap["B"] = std::bind(&reader::readA, this, std::placeholders::_1);
        }
    
        void read()
        {
          const std::string table_name = "A";
          functionCallMap[table_name](table_name);
        }
    };
    
    int main()
    {
        reader r;
        r.read();
    }
    

    我将表名传递给阅读器,使用 bindplaceholder 可以很好地完成。

    【讨论】:

    • 您应该在回调函数中使用字符串引用而不是副本。正如 Moia 所说,你真的应该使用 lambda 而不是 std::bind。关于美学,我不明白为什么您需要功能图,因为readAreadB 是相同的。此外,readB 从未使用过,所以为什么不直接将readA 的代码放入read 中?
    • @Siliace,看来 lambda 和 bind 各有优劣,read A 和 read B 将读取不同类型的表、不同的格式并调用不同的工厂方法。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多