【发布时间】:2015-05-01 12:53:22
【问题描述】:
这是一个代码sn-p:
#include <functional>
#include <iostream>
#include <memory>
template <typename T>
using Callback = std::function<void(const T)>;
template <typename T>
void Call(const T yyy, const Callback<T>& callback) {
callback(yyy);
}
template <typename T>
class MyCallback {
public:
explicit MyCallback(const T counter_init) : counter_{counter_init} {}
void operator ()(const T xxx) {
std::cout << counter_ << '\t' << xxx << std::endl;
++counter_;
}
private:
T counter_;
};
int main() {
const auto callback = std::make_unique<MyCallback<int>>(0);
Call(111, *callback); // expected output is "0 \t 111"
Call(222, *callback); // expected output is "1 \t 222"
return 0;
}
Clang 说它无法将 std::function 与 MyCallback 匹配,而 g++ 认为 MyCallback 是从 Callback 派生的.
clang++ -std=c++14 main.cpp && ./a.out
g++ -std=c++14 main.cpp && ./a.out
我知道解决此问题的最简单方法是使用模板而不是 Callback,因此 Call 将按以下方式定义:
template <typename T, typename F>
void Call(const T yyy, F&& callback) {
callback(yyy);
}
但是对于下一个开发者来说,callback 有哪个签名就不清楚了。
有人能从编译器的角度澄清第一个示例中发生了什么吗?如何在不应用我上面描述的 hack 的情况下解决这个问题?
【问题讨论】:
-
std::function<void(const T)>&&和F&&之间有一个根本区别:第一个强制使用std::function,这是一个多态函数包装器 并且可能有开销。 -
为什么首先要移动回调?将其作为参考有什么问题?
-
如果仅用于文档目的,我建议不要使用
std::function,因为它可能会产生开销。例如,您可以将第二个参数限制为 callable 作为一些void(T)签名。这看起来像这样:template<typename T, typename F, REQUIRE( is_callable<F, void(T)> )> void Call(T const yyy, F&& callback)。也可以仅出于文档目的将其编写为template<typename T, typename F> void Call(const T yyy, callable<F, void(T)>&& callback),无需任何检查,使用template<typename T, typename> using callable = T; -
@dyp,@david-haim,从
Call中删除了右值引用。 -
@KostyaBazhanov 我的第一个(现已删除)评论是错误的,在您的示例中它是
const Callback<T>&还是Callback<T>&&都没有关系:在任何一种情况下,使用像 @987654336 这样的左值调用它@ 将创建一个绑定到函数参数的临时std::function(用左值初始化)。 --const&参数将允许使用std::function类型的左值调用它。