5
votes

I'm working on Multi-type map holder. It works with all primitive types and also with structs e.g. Point. However, if I want to add std::function as another supported type (used for callbacks) then the compiler complains:

MT.cpp:426:15: No viable overloaded '='

MT.h:31:7: Candidate function (the implicit copy assignment operator) not viable: no known conversion from '(lambda at MT.cpp:426:17)' to 'const sharkLib::MT' for 1st argument

MT.h:31:7: Candidate function (the implicit move assignment operator) not viable: no known conversion from '(lambda at MT.cpp:426:17)' to 'sharkLib::MT' for 1st argument

I don't actually overload = operator but instead overload [] with dedicated constructor per supported type.

.h

protected: 
    map<string,MT> valueMap;

public:
    MT (int value);
    MT (std::function<void(Ref*)> ccb);
    virtual MT& operator[] (const char* key);

.cpp

MT::MT (int value)
{
    this->type = ValueType::intValue;
    this->value.int_ = value;
}

MT::MT (std::function<void(Ref*)> value)
{
    this->type = ValueType::ccbValue;
    this->value.ccb_ = value;
}

MT& MT::operator[] (const char* key)
{
    return this->valueMap[key];
}

usage

MT mt;

mt["int"] = 1;
mt["ccb"] = [](Ref *){ CCLOG("Pressed"); };

This last line is the one with error.

2
Possibly answered here: stackoverflow.com/questions/13358672/…Chris
Do I dare ask why ctors and operator[] are on the same type?Yakk - Adam Nevraumont

2 Answers

4
votes

The problem is that you are trying to use a double conversion sequence:

  1. From lambda function to std::function<void(Ref*)>
  2. From std::function<void(Ref*)> to MT

On way around that is to remove the need of the double conversion, using either

mt["cast via function"] = static_cast<std::function<void(Ref*)>([](Ref*){ /*...*/ });
mt["cast via MT"] = MT([](Ref*){ /*...*/ });

If you want to support the conversion from a function type to MT you'd need a constructor of MT which directly takes the function type. Assuming none of your other constructors is written with using an unconstrained template, you could just add

template <typename Fun>
MT::MT(Fun&& fun)
    : type(ValueType::ccbValue) {
    this->value.ccb = std::forward<Fun>(fun);
}

If you are already using an unconstrained template for another type you'd need to use suitable conditions, e.g. std::is_convertible<Fun, std::function<void(Ref*)>>::value, together with a suitable SFINAE-approach to remove the respective constructor(s) from the overload set.

2
votes

Ok, Chris inspired me and this is the solution:

typedef std::function<void(Ref*)> ClickCallback;
...
    MT (ClickCallback ccb);
...
mt["ccb"] = (ClickCallback) [](Ref *){ CCLOG("Pressed "); };;