web-dev-qa-db-ja.com

std :: thread参照渡しのコピーコンストラクター

まあ私はstd :: threadを使用してデータをスレッドに渡すことに問題があります。コピーコンストラクタなどの一般的な意味は理解できたと思いますが、問題点がよくわかりません。コピーコンストラクターを非表示にしたLogというシンプルなクラスがあります。

class Log
{
public:
    Log(const char filename[], const bool outputToConsole = false);
    virtual ~Log(void);

    //modify behavior
    void appendStream(std::ostream *);
    //commit a new message
    void commitStatus(const std::string str);

private:
    //members
    std::ofstream fileStream;
    std::list<std::ostream *> listOfStreams;

    //disable copy constructor and assignment operator
    Log(const Log &);
    Log & operator=(const Log &);
}

今私は主に基づいてメインを持っています http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/echo/blocking_tcp_echo_server.cpp

int main()
{
    static int portNumber = 10000;

    Log logger("ServerLog.txt", true);
    logger.commitStatus("Log Test String");

    try {
        boost::asio::io_service ioService;
        server(ioService, portNumber, logger);
    }
    catch (std::exception &e)
    {
        std::cerr << "Exception " << e.what() << std::endl;
        logger.commitStatus(e.what());
    }

    return 0;
}

Mainが関数サーバーを呼び出し、IOService、portNumber、およびロガーを渡すことがわかります。ロガーは参照によって渡されるため、次のようになります。

using boost::asio::ip::tcp;

void server(boost::asio::io_service &ioService, unsigned int port, Log &logger)
{
    logger.commitStatus("Server Start");

    tcp::acceptor acc(ioService, tcp::endpoint(tcp::v4(), port));

    while(true)
    {
        tcp::socket sock(ioService);
        acc.accept(sock);

        std::thread newThread(session, &sock, logger);
        newThread.detach();
    }

    logger.commitStatus("Server closed");
}

ロガー(またはソケット)を参照によってスレッドに渡そうとするとコンパイラエラーが発生しますが、参照によってセッション()に渡すとエラーが発生しません

static void session(tcp::socket *sock, Log &logger)
{
    std::cout << " session () " << std::endl;
}

今では、参照はポインタを渡すことと同じであることを正しく理解したと思いました。つまり、コピーコンストラクターを呼び出すのではなく、単にポインターを渡すだけです。これにより、構文上、ポインターではないように扱うことができます。

エラーC2248: 'Log :: Log':クラス 'Log'で宣言されたプライベートメンバーにアクセスできません

1>\log.h(55): 'Log :: Log'の宣言を参照

1>\log.h(28):「ログ」の宣言を参照

...

:コンパイルされる関数テンプレートのインスタンス化 'std :: thread :: thread(_Fn、_V0_t &&、_ V1_t)'への参照を参照してください

1>あり

1> [

1>Fn = void(_ cdecl *)(boost :: asio :: ip :: tcp :: socket *、Log&)、

1> _V0_t = boost :: asio :: ip :: tcp :: socket *、

1> _V1_t =ログ&

1>]

しかし、ポインタを渡すように変更すると、すべてが満足です

...
        std::thread newThread(session, &sock, &logger);
...

static void session(tcp::socket *sock, Log *logger)
{
    std::cout << " session () " << std::endl;
}

コピーコンストラクターを呼び出す参照渡しの理由std :: threadのためにここで何か特別なことが起こっていますか?コピーコンストラクターを誤解し、参照で渡しましたか?

例で行われているようにstd :: move()を使用しようとすると、異なるが同様に不可解なエラーが発生します。 VS2012がC++ 11を正しく実装していない可能性はありますか?

29
xaviersjs

std::threadは、引数を値で受け取ります。 std::reference_wrapperを使用すると、参照セマンティクスを取得できます。

std::thread newThread(session, &sock, std::ref(logger));

もちろん、loggerがスレッドよりも長く存続することを確認する必要があります。

58
juanchopanza

参照によってロガー(またはソケット)をスレッドに渡そうとすると、コンパイラエラーが発生します。

スレッドのエントリポイント関数が参照型を取るだけでは不十分です。スレッドオブジェクトitselfは、引数を値で受け取ります。これは、通常、別のスレッドにオブジェクトのコピーが必要なためです。

これを回避するには、_ 参照ラッパー であるstd::ref(logger)を渡して、参照セマンティクスを非表示にしますコピー可能なオブジェクト。