带有 std::function 参数的重载运算符

Posted

技术标签:

【中文标题】带有 std::function 参数的重载运算符【英文标题】:overloaded operator with std::function parameter 【发布时间】:2017-01-04 21:00:36 【问题描述】:

我正在开发多类型地图支架。它适用于所有原始类型,也适用于结构,例如观点。但是,如果我想添加 std::function 作为另一种支持的类型(用于回调),那么编译器会抱怨:

MT.cpp:426:15: 没有可行的重载 '='

MT.h:31:7: 候选函数 (隐式复制赋值运算符)不可行:未知 从“(在 MT.cpp:426:17 处的 lambda)”转换为“const SharkLib::MT” 对于第一个参数

MT.h:31:7:候选函数(隐式移动 赋值运算符)不可行:从 '(lambda at MT.cpp:426:17)' 到 'sharkLib::MT' 作为第一个参数

我实际上并没有重载 = 运算符,而是使用每个支持的类型的专用构造函数重载 []

.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];

用法

MT mt;

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

最后一行是有错误的。

【问题讨论】:

可能在这里回答:***.com/questions/13358672/… 我敢问为什么ctors和operator[]是同一类型吗? 【参考方案1】:

问题是您尝试使用双重转换序列:

    从 lambda 函数到 std::function&lt;void(Ref*)&gt;std::function&lt;void(Ref*)&gt;MT

解决这个问题的方法是消除双重转换的需要,使用任一

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

如果你想支持从函数类型到MT 的转换,你需要一个MT 的构造函数,它直接接受函数类型。假设您的其他构造函数都没有使用不受约束的模板编写,您可以添加

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

如果您已经在为其他类型使用不受约束的模板,则需要使用合适的条件,例如std::is_convertible&lt;Fun, std::function&lt;void(Ref*)&gt;&gt;::value,以及合适的 SFINAE 方法从重载集中删除相应的构造函数。

【讨论】:

为什么不在构造函数中明确包含 sfinae 行? @Yakk:如果构造函数是唯一具有不受约束的模板的构造函数,则它是不必要的,如果不是,则它是不够的,因为其他构造函数需要处理相反的情况。所以把它包括在内似乎毫无意义。 MT const&amp;&amp;MT&amp; 更喜欢 Fun&amp;&amp; 而不是 MT&amp;&amp;MT const&amp; 不是吗?另外,更好的错误信息。 live example。基本上你不应该有一个非 SFINAE 保护的单参数完美转发 ctor。【参考方案2】:

好的,克里斯启发了我,这就是解决方案:

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

【讨论】:

显式 c 风格转换通常是个坏主意。而且你不需要它。

以上是关于带有 std::function 参数的重载运算符的主要内容,如果未能解决你的问题,请参考以下文章

C++11 std::bind和std::function解析

重载 std::function<...>

模板类重载 std::bind 成员函数

为啥用 std::function 重载函数需要中间变量

没有可行的重载'='用于将std :: function回调赋值为成员函数

使用 + (unary plus) 为 lambda 解决函数指针和 std::function 上的模棱两可的重载