32

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");
}

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

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;
}

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

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

4

2 に答える 2

6

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

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

これを回避するには、コピー可能なオブジェクトの下に参照セマンティクスを隠す参照ラッパーstd::ref(logger)である を渡すことができます。

于 2014-01-10T16:44:47.737 に答える