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'

私は何が欠けていますか?

UPDATE2 私はおそらくより良い移動を理解する必要があります-セマンティックな問題は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;
};

コードが機能するunique_ptrの代わりに単純なポインターとしてsocket_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

あなたが述べたように、そのステートメントの2つの可能な解釈がありますが、標準では、このあいまいさの場合、コンパイラはステートメントを関数定義として解釈する必要があることを明示的に義務付けています(括弧の余分なセットを削除します)。

std::thread t(EchoServer socket);

これを強制的に作成するstd::thread場合は、ステートメントを有効な関数宣言ではないようにする括弧のセットを追加できます。

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

初期化には別の構文を使用します。

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

または、C ++ 11を使用しているため、均一な初期化を使用できます。

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

最初の2つのオプションは有効なC++03ですが、C ++ 11コンパイラでは、おそらく3番目のオプションを使用する必要があります(の使用は、std::threadC ++ 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は、一次式の値である引数を使用して呼び出されていますBBまたは、タイプのオブジェクトとなる名前の宣言である可能性がありますA

字句解析プログラムが現在のスコープに表示されている宣言をピアリングできる場合、Aが型名であるか、関数として宣言されているかを判断できます。次に、適切な種類のトークンをパーサーに渡すことができるため、適切な句構造規則が一致します。

于 2012-12-05T23:18:54.810 に答える