LINUX.ORG.RU

'this' in lambda and in class method is not the same when using vector of std::thread

 , , ,


0

3
#include <iostream>
#include <functional>
#include <thread>
#include <vector>

class t {
public:
    int m = 1;
    std::function<void()> f;
    t() {
        f = [this]() {
            std::cout << "2: " << this << std::endl;
            std::cout << m << std::endl;
        };
    }
    void operator()() {
        m = 2;
        std::cout << "1: " << this << std::endl;
        f(); // print 1
    }
};

int main() {
    std::vector<std::thread> threads;
    for(int i = 1; i <= 1; i++) {
        threads.emplace_back(t());
    }
    for (auto &thread : threads) {
        thread.join();
    }
}

threads.emplace_back(t())

Ты неправильно используешь emplace_back, из-за чего в вектор попадает копия создаваемого объекта, с другим this. При этом this, захваченный в f, остается прежним при копировании f.

Замени указанную строку на

threads.emplace_back();

и иди читай про emplace_back.

Siborgium ★★★★ ()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

emplace_back должен был называться construct_back. Комитетчики попуасы.

anonymous ()
Ответ на: комментарий от Siborgium

впринцепи разобрался, не важно что тут использовать, push_back, emplace_back и тд, при создании нового объекта в контейнере, копированием или перемещением, this в ламбде помнит указатель на исходный объект

threads.emplace_back(); - такое не будет работать

babushka ()
Ответ на: комментарий от babushka

this в ламбде помнит указатель на исходный объект

Конечно. Это же не убогий жабоскрипт, где this постоянно скачет и пришлось наваливать костылей, чтобы работало.

ox55ff ★★★★★ ()

Все правильно. При передаче используется конструктор переноса у класса t. Который по умолчанию просто копирует члены исходного объекта в новый. Ты должен представлять лямбду со списком захвата просто как обычный функтор, члены которого - это и есть список захвата. И если их просто скопировать - то член, который содержал значение указателя this просто скопирует свое значение в соответствующий член целевого объекта. Да, там еще этот объект, полученный на основании лямбды копируется в обертку std::function, но сути это не меняет - просто дополнительное копирование добавляется.

rumgot ★★★★★ ()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от babushka

Да, я с телефона неправильно понял код.

Конструктор std::thread в любом случае будет делать move/copy на передаваемом в него объекте, и this будет меняться. Этого можно избежать, если передавать вместо исходного объекта обертку, содержащую ссылку на него, например,

t your_object;
// ...
threads.emplace_back(std::ref(your_object));
Siborgium ★★★★ ()
Ответ на: комментарий от Siborgium

Вариант. Главное, чтобы your_object раньше этого создаваемого потока не умер.

rumgot ★★★★★ ()
Ответ на: комментарий от babushka

как то так в итоге заработало:

#include <iostream>
#include <functional>
#include <thread>
#include <vector>

class t {
public:
	int m = 1;
	std::function<void()> f;
	t() {
		f = [this]() {
			std::cout << "2: " << this << std::endl;
			std::cout << m << std::endl;
		};
	}
	void load() {
		m = 2;
		std::cout << "1: " << this << std::endl;
		f(); // print 1
	}
};

int main() {
	int thread_cnt = 1;
	std::vector<t> objs;
	objs.reserve(thread_cnt);
	std::vector<std::thread> threads;
	for(int i = i; i < thread_cnt; i++) {
		objs.emplace_back();
		threads.emplace_back(&t::load, &objs[i]);
	}
	for (auto &thread : threads) {
		thread.join();
	}
}
babushka ()
Ответ на: комментарий от babushka

вообще-то нормальные люди пишут обертку над std::thread, и получают себе базовый класс thread с виртуальным методом run, и потом уже делают себе треды с нужными телами.

то есть задача - получить базовый класс вида

class Thread {
public:
  virtual int run() = 0; ///тело треда
public:
  bool start(); ///старт треда.
}

а у вас то же самое решение, только с привлечением новых сущностей(эти std::function вообе не нужны тут), и кишками наружу.

alysnix ()
Последнее исправление: alysnix (всего исправлений: 1)
Ограничение на отправку комментариев: только для зарегистрированных пользователей