在CppCon 2015上,来自Microsoft {3}的S. Lavavej避免使用result_of
。但我的情况似乎无法找到合适的替代方案。
请考虑以下代码。有没有办法改变std::result_of<F()>::type
来改为使用decltype
?
#include <iostream>
#include <future>
#include <type_traits>
template<typename T>
struct NopErrCB
{
constexpr T operator() () const { return T(); }
};
template <typename Func, typename ErrCB>
struct SafeTaskWrapper {
Func f;
ErrCB errCB;
template <typename... T>
auto operator()(T&&... args) -> decltype(f(args...)) {
try
{
return f(std::forward<T>(args)...);
}
catch (...)
{
return errCB();
}
}
};
// vvv OVER HERE vvv
template <typename F>
SafeTaskWrapper<F, NopErrCB<typename std::result_of<F()>::type>> make_safe_task(F&& f) {
return { std::forward<F>(f) };
}
template <typename F, typename ErrCB>
SafeTaskWrapper<F, ErrCB> make_safe_task(F&& f, ErrCB&& cb) {
return { std::forward<F>(f), std::forward<ErrCB>(cb) };
}
int main() {
auto futA = std::async(std::launch::async, make_safe_task([] { throw "A"; return 1; }));
auto futB = std::async(std::launch::async, make_safe_task([] { throw "B"; return 1; }, [] { return 2; }));
auto futC = std::async(std::launch::async, make_safe_task([] { throw "C"; }));
std::cout << futA.get() << std::endl;
std::cout << futB.get() << std::endl;
futC.get();
}
<子> PS。请不要介意SafeTaskWrapper的目的。我知道未来已经处理了C ++异常。这只是一个演示,实际代码用于处理Windows SEH异常,但这对于这个问题并不重要。
答案 0 :(得分:4)
您可以使用
decltype(std::declval<F>()())
而不是
typename std::result_of<F()>::type
相似但有细微差别
答案 1 :(得分:4)
是的,您在这里使用result_of
会导致错误。是的,有一种方法可以编写没有result_of
的代码来清除错误。我们可以做到这一点,而无需构建一个等效的错误decltype
替换。
首先,请注意您的result_of
代码有错误。
struct evil {
template<class...Args>
int operator()(Args&&)&&{return 0;}
template<class...Args>
std::string operator()(Args&&)const&{return "hello";}
};
将evil{}
传递给您的代码,它将无法编译。这是因为您在左值上下文中评估了f
中的SafeTaskWrapper
,而您在右值上下文中计算了result_of
。
这正是S.T.L的那种错误。正在谈论。
现在,std::result_of_t<F(Args...)>
基本上 1 等同于decltype( std::declval<F>()( std::declval<Args>()... ) )
,或者在这种情况下decltype( std::declval<F>()())
。在这个意义上用result_of_t
替换decltype
是完全没用的。
我在这里做的是改变你的其余代码。
struct anything_really {
template<class T> operator T() const { return {}; }
};
struct return_anything {
anything_really operator()() const { return {}; }
};
这将取代您的errCB
类型。请注意,它不是模板类型。
接下来,我们必须处理“不要回归无效”的问题。问题。这样做:
template<class R>
struct smart_invoke {
template<class F>
R operator()(F&& f)const {
return std::forward<F>(f)();
}
};
template<>
struct smart_invoke<void> {
template<class F>
void operator()(F&& f)const {
std::forward<F>(f)();
}
};
这为我们提供了最终解决方案:
template <typename Func, typename ErrCB=return_anything>
struct SafeTaskWrapper {
Func f;
ErrCB errCB;
template <class... Ts>
decltype(auto) operator()(Ts&&... args) {
try {
return f(std::forward<T>(args)...);
} catch (...) {
using R=decltype( f( std::forward<T>(args)... ) );
return smart_invoke<R>{}(errCB);
}
}
};
现在我们的工厂正常运作。我可以保留两个,但我很懒。
template <class ErrCB=return_anything, class F>
SafeTaskWrapper<F, ErrCB> make_safe_task(F&& f, ErrCB&& cb = {}) {
return { std::forward<F>(f), std::forward<ErrCB>(cb) };
}
我还交换了模板参数顺序,因此你可以传入一个不同的default-constructible ErrCB
而不用在我使用时传入一个值。
result_of_t
已被淘汰。
你的尾随返回decltype也有错误。它应该完全与(key)return语句相同,不相似。
1 如果F
是成员函数指针或成员等,则存在例外情况,但无论如何您的代码都无法在这些情况下工作。在C ++ 17中,您还希望在std::invoke
和operator()
返回类型子句中调用decltype
。其他更改仍然正确,因为我们避免在两个位置推断返回类型,所以它们保持不变。
答案 2 :(得分:2)
使用std::result_of
的地方。我认为这就足够了。
如果您想要更短且更易读的代码,请考虑使用自{+ 1}以来添加的std::result_of_t
。
//NopErrCB<typename std::result_of<F()>::type>
NopErrCB<std::result_of_t<F()>> // equal
答案 3 :(得分:1)
直接翻译将是:
template <typename F>
SafeTaskWrapper<F, NopErrCB<decltype(std::declval<F>()())>> make_safe_task(F&& f) {
return { std::forward<F>(f) };
}
对我来说这似乎并不清楚。