Есть несколько способов сделать это. Тот, который больше всего похож на код в вопросе, просто так:
a::a() : data(10) {
std::swap(t, std::thread(fn, this));
}
Это потому, что потоки создаются в конструкторе объекта потока. В этом коде t
создается по умолчанию, потому что он не упоминается в списке инициализатора. Код в теле конструктора создает временный объект потока и заменяет его созданным по умолчанию объектом t
. После перестановки t
имеет поток, который выполняет fn(this)
, а временный объект имеет созданный по умолчанию поток. В конце оператора временный объект потока уничтожается.
Вместо этого подхода создания и замены вы также можете создать поток непосредственно в списке инициализатора:
a::a() : data(10), t(fn, this) { // won't work, though...
}
Проблема здесь немного тонкая: несмотря на то, что инициализатор для data
стоит перед инициализатором для t
, конструктор для t
запускается до инициализации data
. Так что есть гонка данных. Это происходит потому, что объекты-члены создаются в порядке объявление ; t
объявляется до data
, поэтому t
создается до инициализации data
. Независимо от их порядка в списке инициализатора конструктора.
Итак, решение состоит в том, чтобы изменить порядок объявлений:
class a {
int data;
std::thread t;
a();
};
Теперь предыдущий код будет работать правильно.
Если вы пишете такой код с важной зависимостью от порядка объявления членов, вы обязаны будущим сопровождающим, включая вас самим, документировать эту зависимость с комментарием:
class a {
// IMPORTANT: data must be declared before t
int data;
std::thread t;
a();
};
Также обратите внимание, что при обоих этих подходах запускаемый поток работает с объектом, конструктор которого еще не завершил работу. Если функция потока использует какие-либо элементы этого объекта, и конструктор изменяет эти элементы после запуска потока, вы получаете гонку данных, следовательно, неопределенное поведение.
В частности, если вы планируете добавить виртуальные функции на a
и переопределить их в производных классах, вы обречены. Когда поток запускается, конструктор базового класса все еще работает, а инициализаторы членов и тело конструктора производного класса не выполняются. Они будут запускать после конструктора базового класса, и опять же, вы глубоко в неопределенном поведении.