在下面的完整测试用例中,如果我使用第一个ctor按值获取仿函数并将其移动到位,则代码将按预期编译并运行。
但是,如果我使用通用引用ctor,则无法编译(我已在下面包含了clang错误消息)。
我该如何解决这个问题或我做错了什么?
#include <functional>
#include <utility>
#include <exception>
template<typename F>
struct py_catch {
F func;
/*
//Works
py_catch(F f)
: func ( std::move(f) )
{ } */
//Doesn't
template<typename F2>
py_catch(F2&& f)
: func ( std::forward<F2>(f) )
{ }
py_catch(py_catch&&)=default;
py_catch(const py_catch&)=default;
py_catch& operator=(const py_catch&)=default;
py_catch& operator=(py_catch&&)=default;
template<typename... Args>
auto operator()(Args&&... args)
-> decltype(func(std::forward<Args>(args)...)) {
try {
return func(std::forward<Args>(args)...);
}
catch(const std::exception&) {
throw;
}
}
};
template<typename F>
py_catch<typename std::remove_reference<F>::type> make_py_catch(F&& f) {
return py_catch<typename std::remove_reference<F>::type>(std::forward<F>(f));
}
int main() {
std::function<void()> s;
s = make_py_catch([]{});
}
编译错误:
testcase2.cpp:16:7: error: no matching constructor for initialization of '<lambda at testcase2.cpp:43:23>'
: func ( std::forward<F2>(f) )
^ ~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:1764:10: note: in instantiation of function template specialization 'py_catch<<lambda at testcase2.cpp:43:23> >::py_catch<py_catch<<lambda at testcase2.cpp:43:23> > &>' requested here
new _Functor(*__source._M_access<_Functor*>());
^
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:1799:8: note: in instantiation of member function 'std::_Function_base::_Base_manager<py_catch<<lambda at testcase2.cpp:43:23> > >::_M_clone' requested here
_M_clone(__dest, __source, _Local_storage());
^
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:2298:33: note: in instantiation of member function 'std::_Function_base::_Base_manager<py_catch<<lambda at testcase2.cpp:43:23> > >::_M_manager' requested here
_M_manager = &_My_handler::_M_manager;
^
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:2173:4: note: in instantiation of function template specialization 'std::function<void ()>::function<py_catch<<lambda at testcase2.cpp:43:23> > >' requested here
function(std::forward<_Functor>(__f)).swap(*this);
^
testcase2.cpp:43:7: note: in instantiation of function template specialization 'std::function<void ()>::operator=<py_catch<<lambda at testcase2.cpp:43:23> > >' requested here
s = make_py_catch([]{});
^
testcase2.cpp:43:23: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'py_catch<<lambda at testcase2.cpp:43:23> >' to 'const <lambda at testcase2.cpp:43:23>' for 1st argument
s = make_py_catch([]{});
^
testcase2.cpp:43:23: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'py_catch<<lambda at testcase2.cpp:43:23> >' to '<lambda at testcase2.cpp:43:23>' for 1st argument
s = make_py_catch([]{});
^
testcase2.cpp:43:23: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided
1 error generated.
答案 0 :(得分:4)
我认为问题是你的转换构造函数模板template<typename F2> py_catch(F2&&)
太贪心了。触发错误的另一种方法是:
int main() {
auto x( make_py_catch([]{}) );
auto y(x);
}
此复制构造使用某种类型py_catch<..>
的左值。 copy-ctor期望py_catch<..> const&
,而你的贪婪模板提供带有py_catch<..>&
类型参数的重载。 [over.ics.rank] / 3中的一条特殊规则现在表示首选采用参考不合格类型的重载。因此,不是copy-ctor,而是调用构造函数模板,它尝试使用整个py_catch<..>
对象(而不是其func
成员)初始化数据成员(lambda)。
一个简单但可能不是最优的解决方案是为非常量左值py_catch(py_catch&) = default;
提供另一个副本。但是,当您使用继承或用户定义的转换时,构造函数模板仍然是首选。
另一种解决方案是在构造函数模板上使用一些SFINAE;例如,检查is_same
,is_base_of
或类似内容(请记住remove_reference
来自F2
的可能引用)。 is_convertible
可能也可以正常工作,但我怀疑它会以递归方式尝试使用构造函数模板进行检查。
答案 1 :(得分:1)
好的,我找到了一个更好的解决方案,暗示了实际问题。
我怀疑问题在于复制和移动ctors因某种原因选择模板而不是默认的ctors。这意味着py_catch<lambda_types?>
被传递给模板ctor并被转发到func
。
所以我使用SFINAE在ctor中添加了一个测试,这解决了这个问题,因为除了匹配func类型之外它会拒绝任何其他内容。
像这样:
template
<
typename F2,
typename =typename std::enable_if<std::is_same<F2, F>::value, F>::type
>
py_catch(F2&& f)
: func ( std::forward<F2>(f) )
{ }
丑陋的是。
我必须等待几天才能将此标记正确,所以如果有人可以告诉我为什么它没有在模板上选择default
ed ctors,那么我会标记为正确。