安全回调提供程序(SFINAE,std::forward 和重载解析)
Safe Callback Provider (SFINAE, std::forward and overload resolution)
我正在研究一种创建 "safe" 回调的机制,在其父对象被销毁后调用时不会导致未定义的行为。 class 应该足够通用以能够包装任何回调,void(...) 回调只是被执行或不被执行,这取决于它们绑定到的对象的状态,以及 return 一个值 return 一个 boost::optional 与 returned 值,如果被执行,或者 boost::none 如果没有 executed.The 实现几乎完成,但是有有两件事让我担心我没有完全理解我的代码...
如果第 19 行未注释掉并且第 18 行被注释掉,模板将无法编译 - 这仅仅是一个可以解决的语法问题,还是我试图错误地使用 result_of 机制(是否std::forward 语义有变化还是多余?)
如果第 88 行未注释掉,第 89 行被注释掉,则由于函数调用 fun 的歧义导致编译失败,我不太明白 - 在我看来 fun(int&&) 是一个确切的匹配,那么为什么编译器会抱怨 fun(int) 版本不明确?
如果还有其他细微(或严重)错误,请一并评论。
谢谢。
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
#include <memory>
#include <boost/optional.hpp>
template<class Func>
class SafeCallback
{
public:
SafeCallback(std::shared_ptr<bool> guard, const Func& callback)
: guard_(guard)
, callback_(callback)
{}
template<class... Args>
// auto operator()(Args&&... args) -> typename std::enable_if<std::is_void<typename std::result_of<Func(std::forward<Args>(args)...)>::type>::value, // won't compile with: 19:91: error: invalid use of template-name 'std::result_of' without an argument list
auto operator()(Args&&... args) -> typename std::enable_if<std::is_void<typename std::result_of<Func(Args...)>::type>::value,
void>::type
{
std::cout << "trying void callback" << std::endl;
if(guard_.lock())
{
std::cout << "callback is still alive :)" << std::endl;
callback_(std::forward<Args>(args)...);
return;
}
std::cout << "uh-oh, callback is dead!" << std::endl;
}
template<class... Args>
auto operator()(Args&&... args) -> typename std::enable_if<!std::is_void<typename std::result_of<Func(Args...)>::type>::value,
boost::optional<typename std::result_of<Func(Args...)>::type>>::type
{
std::cout << "trying non-void callback" << std::endl;
if(guard_.lock())
{
std::cout << "callback is still alive :)" << std::endl;
return callback_(std::forward<Args>(args)...);
}
std::cout << "uh-oh, callback is dead!" << std::endl;
return boost::none;
}
bool isAlive()
{
return guard_.lock();
}
private:
std::weak_ptr<bool> guard_;
Func callback_;
};
class SafeCallbackProvider
{
public:
SafeCallbackProvider()
: guard_(new bool(true))
{}
virtual ~SafeCallbackProvider() = default;
template<class Func>
SafeCallback<Func> makeSafeCallback(const Func& callback)
{
return SafeCallback<Func>(guard_, callback);
}
private:
std::shared_ptr<bool> guard_;
};
struct A : SafeCallbackProvider
{
void fun()
{
std::cout << "---this is fun---" << std::endl;
}
int fun(int&& i)
{
std::cout << "&& this is && " << i << " && fun &&" << std::endl;
return i;
}
// int fun(int i) // fails to compile with: 123:48: error: call of overloaded 'fun(int)' is ambiguous
int fun(int& i)
{
std::cout << "---this is ---" << i << "--- fun---" << std::endl;
return i;
}
};
int main()
{
A* a= new A;
auto cb = a->makeSafeCallback(
[&]()
{
a->fun();
});
cb();
delete a;
cb();
std::cout << "\n----------\n\n";
A* a2= new A;
auto cb2 = a2->makeSafeCallback(
[&](int i)
{
return a2->fun(i);
});
cb2(5);
delete a2;
cb2(5);
std::cout << "\n----------\n\n";
A* a3= new A;
auto cb3 = a3->makeSafeCallback(
[&](int&& i)
{
return a3->fun(std::forward<int>(i));
});
cb3(5);
delete a3;
cb3(5);
return 0;
}
重载的规则是。
函数签名应该不同。
在这两种情况下,编译器都找到相同的签名,尝试更改签名并查看结果。
注意:这只回答了第一个问题,因为我显然有一只苍蝇的注意力。更多即将推出。
std::result_of
本质上是基于看起来像函数 call 的函数 type 执行一些魔法。在有效的行中:
typename std::result_of<Func(Args...)>::type
这是预期用途,模拟调用具有类型 Args...
值的 Func
的实例。另一方面:
typename std::result_of<Func(std::forward<Args>(args)...)>::type
这会将 Args
和 args
扩展为一组 值 ,然后在函数内形成一个 ,
运算符链-style 转换为 Func
。整个事情是一个表达式而不是 std::result_of
期望的类型。
您似乎已经中途使用 decltype
,它看起来像:
decltype(std::declval<Func&>()(std::forward<Args>(args)...))
... 或者,如果您不厌其烦地将它移到 callback_
的声明下方:
decltype(callback_(std::forward<Args>(args)...))
我正在研究一种创建 "safe" 回调的机制,在其父对象被销毁后调用时不会导致未定义的行为。 class 应该足够通用以能够包装任何回调,void(...) 回调只是被执行或不被执行,这取决于它们绑定到的对象的状态,以及 return 一个值 return 一个 boost::optional 与 returned 值,如果被执行,或者 boost::none 如果没有 executed.The 实现几乎完成,但是有有两件事让我担心我没有完全理解我的代码...
如果第 19 行未注释掉并且第 18 行被注释掉,模板将无法编译 - 这仅仅是一个可以解决的语法问题,还是我试图错误地使用 result_of 机制(是否std::forward 语义有变化还是多余?)
如果第 88 行未注释掉,第 89 行被注释掉,则由于函数调用 fun 的歧义导致编译失败,我不太明白 - 在我看来 fun(int&&) 是一个确切的匹配,那么为什么编译器会抱怨 fun(int) 版本不明确?
如果还有其他细微(或严重)错误,请一并评论。
谢谢。
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
#include <memory>
#include <boost/optional.hpp>
template<class Func>
class SafeCallback
{
public:
SafeCallback(std::shared_ptr<bool> guard, const Func& callback)
: guard_(guard)
, callback_(callback)
{}
template<class... Args>
// auto operator()(Args&&... args) -> typename std::enable_if<std::is_void<typename std::result_of<Func(std::forward<Args>(args)...)>::type>::value, // won't compile with: 19:91: error: invalid use of template-name 'std::result_of' without an argument list
auto operator()(Args&&... args) -> typename std::enable_if<std::is_void<typename std::result_of<Func(Args...)>::type>::value,
void>::type
{
std::cout << "trying void callback" << std::endl;
if(guard_.lock())
{
std::cout << "callback is still alive :)" << std::endl;
callback_(std::forward<Args>(args)...);
return;
}
std::cout << "uh-oh, callback is dead!" << std::endl;
}
template<class... Args>
auto operator()(Args&&... args) -> typename std::enable_if<!std::is_void<typename std::result_of<Func(Args...)>::type>::value,
boost::optional<typename std::result_of<Func(Args...)>::type>>::type
{
std::cout << "trying non-void callback" << std::endl;
if(guard_.lock())
{
std::cout << "callback is still alive :)" << std::endl;
return callback_(std::forward<Args>(args)...);
}
std::cout << "uh-oh, callback is dead!" << std::endl;
return boost::none;
}
bool isAlive()
{
return guard_.lock();
}
private:
std::weak_ptr<bool> guard_;
Func callback_;
};
class SafeCallbackProvider
{
public:
SafeCallbackProvider()
: guard_(new bool(true))
{}
virtual ~SafeCallbackProvider() = default;
template<class Func>
SafeCallback<Func> makeSafeCallback(const Func& callback)
{
return SafeCallback<Func>(guard_, callback);
}
private:
std::shared_ptr<bool> guard_;
};
struct A : SafeCallbackProvider
{
void fun()
{
std::cout << "---this is fun---" << std::endl;
}
int fun(int&& i)
{
std::cout << "&& this is && " << i << " && fun &&" << std::endl;
return i;
}
// int fun(int i) // fails to compile with: 123:48: error: call of overloaded 'fun(int)' is ambiguous
int fun(int& i)
{
std::cout << "---this is ---" << i << "--- fun---" << std::endl;
return i;
}
};
int main()
{
A* a= new A;
auto cb = a->makeSafeCallback(
[&]()
{
a->fun();
});
cb();
delete a;
cb();
std::cout << "\n----------\n\n";
A* a2= new A;
auto cb2 = a2->makeSafeCallback(
[&](int i)
{
return a2->fun(i);
});
cb2(5);
delete a2;
cb2(5);
std::cout << "\n----------\n\n";
A* a3= new A;
auto cb3 = a3->makeSafeCallback(
[&](int&& i)
{
return a3->fun(std::forward<int>(i));
});
cb3(5);
delete a3;
cb3(5);
return 0;
}
重载的规则是。
函数签名应该不同。
在这两种情况下,编译器都找到相同的签名,尝试更改签名并查看结果。
注意:这只回答了第一个问题,因为我显然有一只苍蝇的注意力。更多即将推出。
std::result_of
本质上是基于看起来像函数 call 的函数 type 执行一些魔法。在有效的行中:
typename std::result_of<Func(Args...)>::type
这是预期用途,模拟调用具有类型 Args...
值的 Func
的实例。另一方面:
typename std::result_of<Func(std::forward<Args>(args)...)>::type
这会将 Args
和 args
扩展为一组 值 ,然后在函数内形成一个 ,
运算符链-style 转换为 Func
。整个事情是一个表达式而不是 std::result_of
期望的类型。
您似乎已经中途使用 decltype
,它看起来像:
decltype(std::declval<Func&>()(std::forward<Args>(args)...))
... 或者,如果您不厌其烦地将它移到 callback_
的声明下方:
decltype(callback_(std::forward<Args>(args)...))