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

Posted

技术标签:

【中文标题】std::thread 按引用传递调用复制构造函数【英文标题】:std::thread pass by reference calls copy constructor 【发布时间】:2014-01-10 16:05:19 【问题描述】:

嗯,我在使用 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调用了函数server,传递了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?

【问题讨论】:

你从哪里得到它通过引用传递的信息? @zoska:他显然在考虑 session 引用它,完全忘记了中间调用。 由于这个问题被标记为 C++11:您可以/应该使用 delete 关键字隐藏复制构造函数和赋值运算符。 @Ligtness,是的,我完全忘记了中间调用。我猜是愚蠢的错误 @Kit,我尝试使用 delete 关键字禁用复制构造函数和赋值运算符,但由于某种原因它抱怨。关于“之前的意外标记;”我不记得确切的错误信息是什么。我猜 VS2012 还不支持所有 C++11 特性。 【参考方案1】:

std::thread 按值获取参数。您可以使用std::reference_wrapper 获取参考语义:

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

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

【讨论】:

效果很好,谢谢。我不知道 std::ref()。 明确地说,虽然会话函数通过引用获取 Log,但 std::thread 的构造函数通过值获取 Log。【参考方案2】:

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

线程的入口点函数采用引用类型是不够的:线程对象本身通过值来接受它的参数。这是因为您通常希望在单独的线程中复制对象。

要解决这个问题,您可以传递std::ref(logger),这是一个reference wrapper,将引用语义隐藏在可复制对象下。

【讨论】:

以上是关于std::thread 按引用传递调用复制构造函数的主要内容,如果未能解决你的问题,请参考以下文章

thread库,附带程序介绍

带有类参数的 std::thread 初始化导致类对象被多次复制

C++ 多线程std::thread 详解

C++ 多线程std::thread 详解

为啥在通过 const 引用传递临时值时调用复制构造函数?

c++,类的对象作为形参时一定会调用复制构造函数吗?