调试版本SEGFAULT而发布版本有效-是RVO吗

Debug version SEGFAULTs whereas Release version works - is it RVO?

本文关键字:版本 有效 RVO SEGFAULT 布版本 调试      更新时间:2023-10-16

我们有一个模板类,它拥有一些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_;
};

它有两个构造函数,我们制作了一个包装器函数,据说它可以简化事情:

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版本时,应用程序SEGFAULT。我得到的实际错误是:

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?

复制unique_ptr当然有一个根本问题,这是不可能的。这就是独特的意义所在。编译器通过禁用其复制构造函数来强制执行此操作。因此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。也就是说,它也是当前形式的一个潜在的单参数ctor,也应该是explicit