Копирование вызовов конструктора при создании нового потока

Я читаю книгу С++ Concurrency в действии, чтобы узнать больше о потоковом и модуле памяти С++. Мне интересно, сколько раз конструктор копирования вызывается в следующем коде:

struct func
{
    func() = default;
    func(const func& _f) {}

    void operator()() {}
};

int main()
{
    func f;
    std::thread t{ f };
    t.join();

    return 0;
}

Когда я просматриваю этот код в отладчике Visual Studio 2013, я вижу, что экземпляр-конструктор называется четыре раза. Он трижды вызывался из основного потока, а затем один раз из нового. Я ожидал его, поскольку он сделал копию объекта для нового потока. Почему созданы три дополнительных копии?

Ответ 1

Если вы установите точку останова в конструкторе копирования, вы можете увидеть контекст вызова конструктора в окне "Стек вызовов". В режиме отладки я нашел следующие точки при вызове конструктора:

  • Сначала функциональный объект копируется в вспомогательную функцию bind

  • Затем функциональный объект перемещается во внутренний функциональный объект _Bind

  • После этого создается класс для запуска потоков _LaunchPad. В
    конструктор требует ссылки rvalue на экземпляр _Bind, поэтому мы имеем другой вызов конструктора перемещения

  • move конструктор _LaunchPad вызывается, когда его копия создается в новом потоке.

Таким образом, у вас есть 4 вызова конструктора копирования в вашем случае. Если вы добавили конструктор перемещения, вы увидите 1 конструктор копирования и 3 вызова конструктора.

В режиме освобождения все пустые вызовы конструктора устранены, а код ассемблера выглядит довольно просто.