c++ - 构造函数中的通用引用导致失败,无法将可调用仿函数分配给 std::function

标签 c++ templates c++11 universal-reference

在下面的完整测试用例中,如果我使用第一个 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.

最佳答案

我认为问题出在您的转换构造函数模板 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 中的一条特殊规则现在表示采用 reference-to-less-qualified-type 的重载是首选。因此,调用的不是复制构造函数,而是构造函数模板,它尝试使用整个 py_catch<..> 来初始化数据成员 (lambda)。对象(而不是它的 func 成员)。

一个简单但可能不是最佳的解决方案是为非常量左值提供另一个复制函数 py_catch(py_catch&) = default; .但是,当您使用继承或用户定义的转换时,构造函数模板仍然是首选。

另一种解决方案是在构造函数模板上使用一些 SFINAE;例如检查 is_same , is_base_of或类似的东西(记住 remove_reference 来自 F2 的可能引用)。 is_convertible也可能有效,但我怀疑它会递归地尝试使用构造函数模板进行检查。

关于c++ - 构造函数中的通用引用导致失败,无法将可调用仿函数分配给 std::function,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20670337/

相关文章:

python - 立即加载 django 自定义模板标签

c++ - 基于模板参数值的类型

c++ - 多次调用 std::condition_variable::notify_one() 而不进行上下文切换

c++ - 即使存在带有结束条件的重载,带有模板参数包的递归函数也会不断调用自身

c++ - 将自身传递给其他类的构造函数的类,两者均使用模板定义

c++ - 在头文件中包含模板实现cpp文件和链接困惑

c++ - 将 Int 转换为 Char 数组

c++ - std::to_string 不是使用 CygWin 的 C++11 中的成员

c++ - 在我的 cpp 或头文件中找不到错误,类构造失败

c++ - "for(;;)"比 "while (true)"快吗?如果不是,人们为什么要使用它?