std::thread 通过引用传递调用复制构造函数

std::thread pass by reference calls copy constructor

提问人:xaviersjs 提问时间:1/11/2014 更新时间:1/11/2014 访问量:19771

问:

好吧,我在使用 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 和 logger。记录器通过引用传递,因此:

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):参见“Log”的声明

...

:请参阅对正在编译的函数模板实例化“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?

C++ 多线程 C++11 复制构造函数 stdthread

评论

0赞 zoska 1/11/2014
您从哪里获得通过引用传递的信息?
0赞 Lightness Races in Orbit 1/11/2014
@zoska:他显然在考虑引用它,完全忘记了中间调用。session
0赞 Kit Fisto 1/11/2014
由于此问题标记为 C++11:您可以/应该使用 delete 关键字隐藏复制构造函数和赋值运算符。
0赞 xaviersjs 1/11/2014
@Ligtness,是的,我完全忘记了中间电话。我猜是愚蠢的错误
0赞 xaviersjs 1/11/2014
@Kit,我尝试使用 delete 关键字禁用复制构造函数和赋值运算符,但由于某种原因它抱怨了。关于“之前意想不到的令牌”;我不记得确切的错误消息是什么。我猜VS2012还不支持所有C++11功能。

答:

65赞 juanchopanza 1/11/2014 #1

std::thread按值获取其参数。您可以使用以下方法获取引用语义:std::reference_wrapper

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

显然,您必须确保它比线程更长久。logger

评论

5赞 xaviersjs 1/13/2014
效果很好,谢谢。我不知道std::ref()。
1赞 Cloud 8/20/2019
明确地说,尽管会话函数通过引用获取 Log,但 std::thread 的构造函数通过值获取 Log。
6赞 Lightness Races in Orbit 1/11/2014 #2

当我尝试通过引用将记录器(或套接字)传递给线程时,我收到编译器错误

线程的入口点函数仅采用引用类型是不够的:线程对象本身按值获取其参数。这是因为您通常希望在单独的线程中复制对象。

为了解决这个问题,你可以传递 ,这是一个在可复制对象下隐藏引用语义的引用包装器std::ref(logger)