4

如果我写了这行代码:

std::thread t(EchoServer(socket));

编译器如何解释这条指令?它可以是一个函数声明或只是一个初始化。我有以下代码:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>

#include <boost/asio.hpp>

typedef boost::asio::ip::tcp::socket Socket;
auto socket_deleter = [] (Socket* s) {s->close(); delete s;};
typedef std::unique_ptr<Socket, decltype(socket_deleter)> socket_ptr;

class EchoServer {
public:
    static void Listen(unsigned int port)
    {

        using namespace std;
        using namespace boost::asio;

        io_service ios;

        // create an endpoint to listen to a certain port
        ip::tcp::endpoint endpoint(ip::tcp::v4(), port);

        cout << "Listening to TCP Socket on port " << port << " ..." << endl;

        // Start opening a socket
        ip::tcp::acceptor acceptor(ios, endpoint);

        // this loop must be infinite... but we accept only 3 connections
        auto socket = socket_ptr(new Socket(ios));

        std::thread t(EchoServer(socket));
    }

    EchoServer(socket_ptr&& s) : m_socket(std::move(s))
    {
    }

    void operator ()() const
    {
    }

private:
    socket_ptr m_socket;
};

但是编译器给了我以下警告:

C4930: 'std::thread t(EchoServer(socket))': std::thread t(EchoServer(socket)) function not called (was a variable definition intended?).

那么我该如何解释这一行是 std::thread 创建类型的对象而不是函数声明。

更新 1: 我正在使用不支持统一初始化的 Visual Studio 2012,因此我将代码从更改为std::thread t((EchoServer(socket)));std::thread t((EchoServer(socket)));但这次我有一个我不明白的编译时错误:

error C2440: '<function-style-cast>': cannot convert from 'std::unique_ptr<_Ty,_Dx>' to 'EchoServer'

我错过了什么?

更新 2 我可能必须更好地理解移动语义问题在于 socket_ptr 的声明。我已经以这种(丑陋的)方式更改了代码......但现在可以编译了。

#include <iostream>
#include <thread>
#include <boost/asio.hpp>

typedef boost::asio::ip::tcp::socket Socket;
auto socket_deleter = [] (Socket* s) {s->close(); delete s;};
/*
typedef std::unique_ptr<Socket, decltype(socket_deleter)> socket_ptr;
*/
typedef Socket* socket_ptr;

class EchoServer {
public:
    static void Listen(unsigned int port)
    {

        using namespace std;
        using namespace boost::asio;

        io_service ios;

        // create an endpoint to listen to a certain port
        ip::tcp::endpoint endpoint(ip::tcp::v4(), port);

        cout << "Listening to TCP Socket on port " << port << " ..." << endl;

        // Start opening a socket
        ip::tcp::acceptor acceptor(ios, endpoint);

        // this loop must be infinite... but we accept only 3 connections
        auto socket = new Socket(ios);

        std::thread t((EchoServer(socket)));
    }

    EchoServer(socket_ptr s) : m_socket(s)
    {
    }

    ~EchoServer()
    {
        m_socket->close();
        delete m_socket;
    }

    void operator ()() const
    {
    }

private:
    socket_ptr m_socket;
};

将 socket_ptr 更改为简单的指针,而不是代码工作的 unique_ptr。

4

5 回答 5

7

这是一个函数声明。如果要声明直接初始化的对象,可以使用以下方法之一:

std::thread t(EchoServer { socket });
std::thread t { EchoServer(socket) };
std::thread t { EchoServer { socket} };
std::thread t((EchoServer(socket)));

大括号初始化是明确的初始化,在最后一行你有一个带括号的表达式,它不能作为函数参数声明。

于 2012-12-05T23:18:39.073 回答
4

正如您所提到的,该语句有两种可能的解释,但标准明确规定,在这种歧义的情况下,编译器必须将该语句解释为函数定义(删除额外的括号集):

std::thread t(EchoServer socket);

如果您想强制创建 astd::thread您可以添加一组额外的括号,使该语句不是有效的函数声明:

std::thread t((EchoServer(socket)));

对初始化使用不同的语法:

std::thread t = std::thread(EchoServer(socket));

或者由于您使用的是 C++11,您可以使用统一初始化

std::thread t{EchoServer(socket)};

前两个选项是有效的 C++03,但在 C++11 编译器中您可能应该使用第三个选项(并且使用std::thread表示您正在使用 C++11 功能)

于 2012-12-05T23:20:22.457 回答
3

在 C++ 2011 中,给定情况下最简单的方法是用大括号替换括号:

std::thread t{EchoServer(socket)};

但是请注意,这保证会调用std::terminate(),因为线程既不分离也不连接。

于 2012-12-05T23:18:49.310 回答
1

static_cast可以用来触发用户定义的转换,所以试试

std::thread t(static_cast<EchoServer>(socket));

而不是强制转换的构造函数调用语法。

要修复转换失败,请将构造函数更改为:

EchoServer(socket_ptr&& s) : m_socket(s)
{
}
于 2012-12-05T23:17:58.970 回答
1

C 和 C++ 编译器必须在解析时构建关于声明和范围的语义知识,并查阅该知识以了解如何解析某些内容。

扫描标识符时,可以将其转换为词法类别的标记,具体取决于该标识符相对于扫描发生的范围的声明方式。

一个比你的更简单的例子是:

A ( B );

这可能是一个函数调用:函数A被调用的参数是主表达式的值B。或者,它可能是 name 的声明B,它是 type 的对象A

如果我们的词法分析器能够查看在当前范围内可见的声明,它可以确定 A 是否是类型名称,或者它是否被声明为函数。然后,它可以将适当种类的标记传递给解析器,因此适当的短语结构规则将匹配。

于 2012-12-05T23:18:54.810 回答