Как передать список в поток, не копируя его, уничтожая при этом оригинал - PullRequest
4 голосов
/ 20 сентября 2019

Этот код создает и выполняет поток, которому необходимо получить содержимое std::list<int> data без копирования содержимого этого списка и изменения его адреса в памяти.Указатели на список должны оставаться действительными для другого кода.

После создания потока исходный список data уничтожается в основном потоке, поэтому я не могу использовать его адрес в этом случае.

Как мне это сделать?Я пытался использовать ссылки rvalue, хотя я волнуюсь, что у меня может быть висящий указатель, если я попытаюсь сделать это таким образом.Вот что у меня есть:

void func(std::list<int> && data){  // <-- what should go here as an argument?
   std::sort(data.begin(), data.end());
   // do stuff with data
}

int main(){
    {
        std::list<int> data;
        data.push_back(1);
        std::thread(func, std::move(data)).detach();
    } 
    // data destroyed
}

1 Ответ

4 голосов
/ 20 сентября 2019

Конструктор thread вызывает

std::invoke(decay_copy(std::forward<Function>(f)), 
            decay_copy(std::forward<Args>(args))...);

, где decay_copy определяется следующим образом:

template <class T>
std::decay_t<T> decay_copy(T&& v) { return std::forward<T>(v); }

Вы передаете move(data).Поскольку decay_copy использует пересылку ссылок, поэтому он возвращает T&&, потому что decay_t отбрасывает константность и ссылочность, новый объект списка создается с помощью конструктора перемещения.Поэтому список data из main был перемещен в конструктор потока (thread сохраняет свои параметры в кортеже - вам не нужно заботиться о времени жизни перемещенного объекта списка данных. Теперь вопрос заключается в том, как вы хотите передать/ access перемещен data список в func. По копированию / перемещению или ссылке, подробности ниже).

Теперь вызывается invoke:

invoke (func, temporary list object returned by decay_copy)

и здесь есть дваследующие случаи:

[1] func имеет подпись func(list<int>&&), в этом случае вы просто обращаетесь к объекту перемещенного списка по ссылке - ни копирование, ни операции перемещения не вызываются.

[2] func имеет подпись func(list<int>), в этом случае конструктор перемещения будет вызываться из списка.Если список не имеет поддержки перемещения, здесь будет вызвана операция копирования.

Поэтому используйте list<int>&&, вы сохраните одну операцию перемещения.

...