传递 unique_ptr 时调试和发布配置的移动不一致?
Inconsistent move in debug and release configurations while passing unique_ptr around?
所以我得到了一些使用 SFML 库处理一些简单的 tcp 套接字的代码。因此,在使用 SFML 功能的情况下创建套接字,并从函数 returned 作为右值引用。
一个组织函数然后将这个套接字传递给(目前只被存储)并通知它的调用者一个套接字是否被处理。然而,这并不像预期的那样工作。
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
的class在组合中封装了一个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
test\test_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()
的第一个 return,即 return 被侦听器接受的套接字,已经到达,但在随后的外部processNextIncoming
的函数 new_socket
的值不是 set/is nullptr
.
在 Release 中,即 g++ -O3
输出显示 promise,但测试本身因段错误而崩溃,似乎在测试拆卸中,可能是在释放套接字时,我通过进一步的输出确定,作为调试在优化代码中不是很富有成果。
[==========] 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
的构造似乎在returning:
之前再次到达
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);
}
//...
}
问题是您正在 return 引用局部变量 (new_socket
)。不要因为它是一个右值引用而分心——它仍然是一个引用!您应该 return unique_ptr
按值代替。而且,即使 std::move()
您正在 returning 的值是合法的,它充其量是无用的,或者在最坏的情况下会错过优化 - 所以只需要 return new_socket
.
所以我得到了一些使用 SFML 库处理一些简单的 tcp 套接字的代码。因此,在使用 SFML 功能的情况下创建套接字,并从函数 returned 作为右值引用。 一个组织函数然后将这个套接字传递给(目前只被存储)并通知它的调用者一个套接字是否被处理。然而,这并不像预期的那样工作。
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
的class在组合中封装了一个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
test\test_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()
的第一个 return,即 return 被侦听器接受的套接字,已经到达,但在随后的外部processNextIncoming
的函数 new_socket
的值不是 set/is nullptr
.
在 Release 中,即 g++ -O3
输出显示 promise,但测试本身因段错误而崩溃,似乎在测试拆卸中,可能是在释放套接字时,我通过进一步的输出确定,作为调试在优化代码中不是很富有成果。
[==========] 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
的构造似乎在returning:
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);
}
//...
}
问题是您正在 return 引用局部变量 (new_socket
)。不要因为它是一个右值引用而分心——它仍然是一个引用!您应该 return unique_ptr
按值代替。而且,即使 std::move()
您正在 returning 的值是合法的,它充其量是无用的,或者在最坏的情况下会错过优化 - 所以只需要 return new_socket
.