Почему я не могу переместить std:: unique_ptr внутри lambda в С++ 14?

Я хочу передать необработанный указатель внутри лямбда, но я не хочу, чтобы он просочился, если лямбда не вызывается. Это выглядит так:

void Clean(std::unique_ptr<int>&& list);

void f(int* list) {
  thread_pool.Push([list = std::unique_ptr<int>(list) ] {
    Clean(std::move(list));  // <-- here is an error.
  });
}

Я получаю сообщение об ошибке в Clang 3.7.0:

error: привязка ссылки к типу 'unique_ptr < [2 *...] > ' к значению типа 'unique_ptr < [2 *...] > ' drops qualifiers

Но я не вижу каких-либо квалификаторов в первую очередь, особенно отброшен.

Кроме того, я нашел аналогичный отчет в списке рассылки, но без ответа.


Как мне изменить свой код, чтобы он скомпилировался и работает как ожидается по семантике?

Ответы

Ответ 1

Вам нужно сделать внутреннюю лямбда mutable:

[this](Pointer* list) {
  thread_pool.Push([this, list = std::unique_ptr<int>(list) ]() mutable {
                                                               ^^^^^^^^^
    Clean(std::move(list));
  });
};

operator() по lambdas const по умолчанию, поэтому вы не можете изменять его членов в этом вызове. Таким образом, внутренний list ведет себя так, как если бы он был const std::unique_ptr<int>. Когда вы выполняете кастинг move, он преобразуется в const std::unique_ptr<int>&&. Вот почему вы получаете ошибку компиляции об отбрасывании квалификаторов: вы пытаетесь преобразовать ссылку на константу rvalue в ссылку, не связанную с константой rvalue. Ошибка может быть не столь полезной, как может быть, но все сводится к: вы не можете move a const unique_ptr.

mutable исправляет, что - operator() больше не const, поэтому проблема больше не применяется.

Примечание: если ваш Clean() взял unique_ptr<int> вместо unique_ptr<int>&&, что имеет больше смысла (поскольку это более явный, детерминированный приемник), тогда ошибка была бы намного более очевидной:

error: call to deleted constructor of `std::unique_ptr<int>`
note: 'unique_ptr' has been explicitly marked deleted here  

    unique_ptr(const unique_ptr&) = delete
    ^