传递unique_ptr时调试和发布配置中的移动不一致



所以我得到了一些使用SFML库处理一些简单tcp套接字的代码。因此,在使用SFML功能的情况下创建了一个套接字,并从函数返回作为右值引用。然后,一个组织函数传递这个套接字(目前只存储(,并向调用方发出套接字是否已处理的信号。然而,这并没有如预期的那样起作用。

struct TcpSocket : public ::sf::TcpSocket {};
unique_ptr<TcpSocket>&& TcpListener::nonBlockingNext() 
{
unique_ptr<TcpSocket> new_socket (new TcpSocket) ;
listener.setBlocking(false);
if( listener.accept(*new_socket) == ::sf::Socket::Status::Done) 
{
new_socket->setBlocking(false);
std::cout << "Connection established! " << new_socket.get() << "n";
return std::move(new_socket);
}
return std::move( unique_ptr<TcpSocket>(nullptr) );
}
bool ConnectionReception::processNextIncoming()
{
unique_ptr<TcpSocket> new_socket (listener.nonBlockingNext());
std::cout << " and then " << new_socket.get() << "n";
if( !new_socket ) return false;
processNewTcpConnection( ::std::move(new_socket) );
return true;
}

前面使用的TcpListener类在composition中封装了一个sf::TcpListener,并简单地转发它的用法。

我有一个简单的测试,尝试连接。

TEST(test_NetworkConnection, single_connection)
{
ConnectionReception reception;
reception.listen( 55555 );
std::this_thread::sleep_for( 50ms );
TcpSocket remote_socket;
remote_socket.connect( "127.0.0.1", 55555 );
std::this_thread::sleep_for( 10ms );
EXPECT_TRUE( reception.processNextIncoming() );
}

这个测试在我编译它的两种配置中失败的方式不同。在调试(g++ -g3(中,测试意外失败。

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from test_NetworkConnection
[ RUN      ] test_NetworkConnection.single_connection
Connection established! 0x6cf7ff0
and then 0
testtest_NetworkConnection.cpp:24: Failure
Value of: reception.processNextIncoming()
Actual: false
Expected: true
[  FAILED  ] test_NetworkConnection.single_connection (76 ms)
[----------] 1 test from test_NetworkConnection (78 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (87 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] test_NetworkConnection.single_connection
1 FAILED TEST

调试和输出表明,nonBlockingNext()的第一个返回,即返回侦听器接受的套接字的返回,已经达到,但在processNextIncoming的后续外部函数中,new_socket的值没有设置/为nullptr

在Release中,也就是说g++ -O3的输出显示出了希望,但测试本身出现了segfault,似乎是在测试拆卸中,可能是在释放套接字时,我通过进一步的输出确定了这一点,因为在优化的代码中调试并不是很有成效。

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from test_NetworkConnection
[ RUN      ] test_NetworkConnection.single_connection
Connection established! 0xfe7ff0
and then 0xfe7ff0

我进一步注意到,在-g3编译中调试时,"nonBlockingNext(("中new_socket的构造似乎在返回之前再次达到:

Thread 1 hit Breakpoint 1, network::TcpListener::nonBlockingNext (this=0x640f840)
at test/../src/NetworkConnection.hpp:40
40          unique_ptr<TcpSocket> new_socket (new TcpSocket) ;
(gdb) n
41          listener.setBlocking(false);
(gdb)
42          if( listener.accept(*new_socket) == ::sf::Socket::Status::Done)
(gdb)
44              new_socket->setBlocking(false);
(gdb)
45              std::cout << "Connection established! " << new_socket.get() << "n";
(gdb)
Connection established! 0x6526340
46              return std::move(new_socket);
(gdb)
40          unique_ptr<TcpSocket> new_socket (new TcpSocket) ;                <<<<<<--------- here
(gdb)
49      }
(gdb)
network::ConnectionReception::processNextIncoming (this=0x640f840) at test/../src/NetworkConnection.hpp:79
79          std::cout << " and then " << new_socket.get() << "n";
(gdb)
and then 0
80          if( !new_socket ) return false;
(gdb)

一个步骤,很可能是在发布配置中优化掉的,或者可能只是gdb的怪异。

出了什么问题?我该如何继续并使其发挥作用?我在右倾和移动方面犯了什么错误吗?

您在这里有未定义的行为:

unique_ptr<TcpSocket>&& TcpListener::nonBlockingNext() 
{
unique_ptr<TcpSocket> new_socket (new TcpSocket) ;
//...
if( /*...*/) 
{
//...
return std::move(new_socket);
}
//...
}

问题是,您正在返回对局部变量(new_socket(的引用。不要因为它是一个右值引用而分心——它仍然是一个引用!您应该按值返回unique_ptr。而且,即使您返回的值对std::move()是合法的,但往好了说是无用的,往坏了说是错过了优化——所以只使用return new_socket

最新更新