我们有一个模板类,其中包含一些std::unique_ptr
,其中一些是boost::asio
template <class cloud_type,
bool keep_alive = true,
class socket_type = asio_http,
class error_handle = default_error_handler>
class callable
{
callable() = delete;
// other stuff here, click the link to see actual code
private:
// other members, most are unique pointers
std::unique_ptr<boost::asio::io_service> io_;
};
它有two constructors,我们制作了一个包装函数,可以简化事情:
template <class cloud_type,
bool keep_alive = true,
class socket_type = asio_http,
class error_handle = default_error_handler,
class ...args,
typename =
typename std::enable_if<!std::is_same<cloud_type,
cloud_batch>::value, bool>>
callable<cloud_type,keep_alive,socket_type,error_handle>
call(typename cloud_type::callback functor,
args... params)
{
return callable<cloud_type,
keep_alive,
socket_type,
error_handle>(functor, default_node, params...);
}
我们两个人在Ubuntu 16.04上使用G ++ 5.9和Boost 1.58对此进行了测试。
当我们构建Release
版本(-O3
)时,应用程序运行正常。
但是,当我们构建Debug
版本时,应用程序SEGFAULTs。
我得到的实际错误是:
Thread 1 "human_detection" received signal SIGSEGV, Segmentation fault.
0x0000000000443128 in std::unique_ptr<boost::asio::io_service, std::default_delete<boost::asio::io_service> >::get (this=0x120) at /usr/include/c++/5/bits/unique_ptr.h:305
305 { return std::get<0>(_M_t); }
这似乎源于:
#0 0x0000000000443128 in std::unique_ptr<boost::asio::io_service, std::default_delete<boost::asio::io_service> >::get (this=0x120) at /usr/include/c++/5/bits/unique_ptr.h:305
#1 0x0000000000441880 in std::unique_ptr<boost::asio::io_service, std::default_delete<boost::asio::io_service> >::operator-> (this=0x120) at /usr/include/c++/5/bits/unique_ptr.h:298
#2 0x000000000043f2b4 in noos::cloud::callable<noos::cloud::human_detection, false, noos::cloud::asio_http, noos::cloud::default_error_handler>::send (this=0x0, timeout=0) at /home/zuperath/code/noos-api-maria/./noos/cloud/callable.tpl:120
指向:
void callable<cloud_type,
keep_alive,
socket_type,
error_handle
>::send(unsigned int timeout)
{
assert(socket_ && query_ && resol_ && io_);
if (!socket_)
throw std::runtime_error("socket not set");
if (!io_ || !query_ || !resol_)
throw std::runtime_error("io, query or resolver not set");
object.fill_buffer(boost::ref(*buffer_.get()), endpoint);
socket_->is_connected() ?
socket_->send(*query_.get(), *resol_.get(), timeout, *buffer_.get()) :
socket_->begin(*query_.get(), *resol_.get(), timeout);
io_->run();
io_->reset(); // here !!!
}
我试图了解我们做错了什么,并且我猜测Release
使用了RVO,而Debug
制作的副本导致了上述?
当我调用包装器时:
auto query = call<human_detection,false>(
[&](std::vector<noos::object::human> humans) {
std::cout << "Found " << humans.size() << " humans!" << std::endl;
}, pic);
错误在Debug
期间仍然存在,而如果我直接调用类构造函数,则SEGFAULT会消失:
callable<human_detection,false> query([&](std::vector<noos::object::human> humans) {
std::cout << "Found " << humans.size() << " humans!" << std::endl;
}, default_node, pic);
我担心类的资源存在潜在问题(例如,boost::asio::io
),这是通过使类可复制而启用的。
我解决了SEGFAULT,这是由于一个lambda通过引用捕获,一个( this )正在被释放。 但是,我原来的问题仍然存在;为什么这会在DEBUG期间发生而不是RELEASE?
答案 0 :(得分:2)
复制unique_ptr
当然存在根本问题,但这是不可能的。这是唯一的重点。编译器通过禁用其复制ctor来强制执行此操作。因此,callable
的默认复制构造函数也不存在。
但是看起来就像您的callable
可以被复制一样。我怀疑发生的事情是你意外地写了一个转换构造函数:
callable::callable(callback functor, platform = default_node);
我怀疑您的callable
可以转换为callback
,并最终通过此路线进行复制。
根据经验,可以使用一个参数调用的构造函数(可能在添加默认值之后)应为explicit
。
BTW:您的其他ctor也有逻辑错误:
template <typename... parameters>
callable(callback functor,
platform info = default_node,
parameters... args);
究竟如何使用此默认值?它只能在提供单个参数时使用,但随后会出现重载决策,并且会选择第一个ctor。也就是说,它也是当前形式的潜在单一论证者,也应该是explicit
。