首页 > 解决方案 > 传递 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在组合中封装了一个 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() );
}

此测试在我正在编译的两种配置中失败的方式不同。在 debug ( 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(),即返回监听器接受的套接字的那个,已经到达,但在随后的外部函数中processNextIncoming的值new_socket不是 set/is nullptr

在 Release 中,g++ -O3输出显示有希望,但测试本身因段错误而崩溃,似乎在 test-teardown 中,可能是在释放套接字时,我通过进一步的输出确定了这一点,因为在优化代码中进行调试并不是很有成果。

[==========] 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 编译中调试时,我进一步注意到,new_socket在返回之前似乎再次达到了“nonBlockingNext()”中的构造:

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 很奇怪。

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

标签: c++socketsrvaluestdmove

解决方案


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

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.


推荐阅读