简体   繁体   中英

std::function in std::unordered_map, error C3867 non-standard syntax; use '&' to create a pointer to member

I try to replace if/else by using a map to call some api

why the initializer list throws the error?

...
#include <functional>
#include <unordered_map>

class MyClass final : public Base
{
   std::string InfoInquiry(Class_1& request, Class_2& response);
   std::string ActivateDeactivate(Class_1& request, Class_2& response);
   ...
   using Callback = std::function<std::string(Class_1&, Class_2&)>;
   const std::unordered_map<std::string, Callback> m_ApiCalbacks{ //error C3867: 'MyClass ::InfoInquiry': non-standard syntax; use '&' to create a pointer to member
      {"INFO", InfoInquiry},
      {"ACTIVATE_DEACTIVATE", ActivateDeactivate},
      ...
   };
}

note: static is not allowed in this case because it's in a dll and reloading the dll gets problems

I would argue for using pointers-to-members for this. std::function comes with a cost (at least one extra indirection, ie slow).

Something like this (edited for brevity):

#include <functional>
#include <map>
#include <iostream>

class MyClass
{
public:
    std::string InfoInquiry() { return "info!"; }
    std::string ActivateDeactivate() { return "activate!"; }
    using Callback = std::string (MyClass::*) ();
    const std::map<std::string, Callback> m_ApiCalbacks {
       {"INFO", &MyClass::InfoInquiry},
       {"ACTIVATE_DEACTIVATE", &MyClass::ActivateDeactivate}
    };
    std::string Call(std::string const& name) {
        auto it = m_ApiCalbacks.find(name);
        if (it == m_ApiCalbacks.end()) // optional check if name is valid
            return "???";
        return (this->*(it->second))();
    }
};

int main() {
    MyClass c;
    std::cout << c.Call("INFO") << "\n";
    std::cout << c.Call("ACTIVATE_DEACTIVATE") << "\n";
    std::cout << c.Call("X") << "\n";
}

Bonus note: in some C++ implementations, std::map is faster than std::unordered_map . Check to be sure. ( Live demo )

Part of what isn't said (except in some replies) is that you're trying to call member functions, and that requires you to have a pointer (or reference, or whatever) to the class instance you're calling. You can wrap that up into your map if you wish. You don't need to in this case, as your map "lives" inside the class, and thus you don't have to, but here's a way to do it if you'd like.

I'm taking rustyx's implementation, and changing it slightly to show another way of doing this, preserving your original signatures of the callee methods.

#include <functional>
#include <map>
#include <iostream>

class MyClass
{
public:
    std::string InfoInquiry(Class_1& request, Class_2& response) { return "info!"; }
    std::string ActivateDeactivate(Class_1& request, Class_2& response) { return "activate!"; }
    using Callback = std::function<std::string(Class_1&, Class_2&)>;
    const std::map<std::string, Callback> m_ApiCalbacks {
       {"INFO", std::bind(&MyClass::InfoInquiry, this, std::placeholders::_1, std::placeholders::_2)},
       {"ACTIVATE_DEACTIVATE", std::bind(&MyClass::ActivateDeactivate, this, std::placeholders::_1, std::placeholders::_2)}
    };
    std::string Call(std::string const& name, Class_1& c1, Class_2& c2) {
        auto it = m_ApiCalbacks.find(name);
        if (it == m_ApiCalbacks.end()) // optional check if name is valid
            return "???";
        return (it->second)(c1, c2);
    }
};

int main() {
    MyClass c;
    Class_1 local_c1{};
    Class_2 local_c2{};
    std::cout << c.Call("INFO", local_c1, local_c2) << "\n";
    std::cout << c.Call("ACTIVATE_DEACTIVATE", local_c1, local_c2) << "\n";
    std::cout << c.Call("X", local_c1, local_c2) << "\n";
}

This preserves the ability to call the methods with arbitrary arguments through the usage of std::bind to make a callable function that has the signature you want, while also keeping a pointer to your instance. If you ever do this outside of your class though, be sure you don't use a dangling pointer. Here, this is used and your map lives as long as your class, but if your bind target is another class, make sure it doesn't point to an object that doesn't exist anymore when you try and call it.

The error states, what to do. But it only works on static functions that simple.

class MyClass : public Base {
    static std::string InfoInquiry(Class_1& request, Class_2& response);
    static std::string ActivateDeactivate(Class_1& request, Class_2& response);

    using Callback = std::function<std::string(Class_1&, Class_2&)>;
    const std::unordered_map<std::string, Callback> m_ApiCalbacks {
        {"INFO", &MyClass::InfoInquiry},
        {"ACTIVATE_DEACTIVATE", &MyClass::ActivateDeactivate},
    };
};

Since m_ApiCalbacks is const, using static member functions makes sense.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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