2017-06-06 70 views
8

Kompilator mówi, że próbuję uzyskać dostęp do usuniętej funkcji (tj. Konstruktora kopii wyrażenia lambda). Ale nie widzę gdzie.Dlaczego nie mogę przenieść elementu do wektora niekopiowych?

std::vector<std::function<void()>> tasks; 
std::packaged_task<int()> task{ [] { return 1; } }; 
tasks.emplace_back(
    [ t = std::move(task) ]() mutable { t(); }); 

(code is also here)

(próbuję dowiedzieć się, dlaczego oni korzystać shared_ptr<task> w https://www.slideshare.net/GlobalLogicUkraine/c11-multithreading-futures).

Na GCC i MSVC otrzymuję ten sam błąd - Obawiam się, że robię coś złego ...

error: use of deleted function 
'main()::<lambda()>::<lambda>(const main()::<lambda()>&)' 

Dlaczego nie mogę emplace to std::function na wektorze?

+1

[Jak przechowywać niezadowalną funkcję std :: do pojemnika?] (/ Questions/28208948/how-to-store-non-copyable-stdfunction-into-a-container) – cpplearner

+0

@cpplearner thanks, so it wszystko sprowadza się do funkcji dyktującej CopyConstructable? – xtofl

Odpowiedz

4

Od cppreference:

F muszą spełniać wymagania wywołania i CopyConstructible

przypadku F jest rodzaju funkcja służąca do skonstruowania std::function. Jednak std::packaged_task jest not copy constructible. Tak więc na liście przechwytywania t nie jest możliwe do kopiowania i jest niestatycznym elementem lambda, co powoduje, że niejawny konstruktor kopii dla usuniętej lambda.

+0

Rozumiem, że nie można kopiować, ale ... ale ... nie chcę używać konstruktora kopiowania. Dlaczego 'emplace_back ([t] {})' potrzebuje konstruktora kopii? – xtofl

+0

Ah! Jest to wywoływany konstruktor 'function (F)', ponieważ jestem 'emplace'ing a lambda! Rozumiem. Dzięki. – xtofl

2

Krótka odpowiedź: Lambdas i std::packaged_task nie są std::function s.

Długa odpowiedź, nie można przenieść std::packaged_task w std::function

Oto co mi oferuje jako rozwiązanie:

std::vector<std::packaged_task<int()>> tasks; 
std::packaged_task<int()> task{ []() mutable { return 1; } }; 
tasks.emplace_back(std::move(task)); 

Jeśli rzeczywiście potrzebują std :: funkcję, a nie byle wymagalne, musisz powiązać lambda do std::function

0

Konstruktor obiektu std::function wymaga, aby przekazany obiekt funkcji był CopyConstructible, ale nie jest to std::packaged_task<F> (dla żadnego F). std::function wykonuje wymazanie typu , w którym typ dynamiczny nie jest widoczny w typie statycznym. Rozważmy np .:

int invoke(std::function<int()> f) { return f(); } 

int main() 
{ 
    std::packaged_task<int()> p{/*etc*/}; 
    auto l = [] { return 5; }; 
    std::function<int()> f(/* either p or l */); 
    std::cout << invoke(f) << '\n'; 
} 

Wezwanie do invoke wymaga kopiowania f (przechodzą przez wartość). Jednak kod f jest kopiowalny, jeśli został wykonany z l, ale nie można go skopiować, jeśli został wykonany z p, a to nie ma nic wspólnego z typem statycznym f. Istnieją zasadniczo trzy podejścia do tego problemu:

  • Zakaz kopiowania std::function podczas kompilacji.
  • Zezwalaj na kopiowanie std::function w czasie kompilacji, ale rzuć błąd czasu wykonywania, jeśli zawartego typu nie można skopiować.
  • Umożliwia kopiowanie std::function w czasie kompilacji i wymaga, aby każdy obiekt funkcji, który został w nim umieszczony, był kopiowalny.

Podejście nr 1 jest bardzo restrykcyjne w zakresie sposobu przechowywania, przekazywania i udostępniania funkcji oraz w zasadzie zabrania stosowania typowych przypadków użycia na rzecz rzadkiego przypadku użycia obiektu funkcji, który nie może być kopiowany.

Podejście nr 2 jest problematyczne, ponieważ użytkownicy musieliby być wykształceni, że kopiowanie std::function może się nie udać w niektórych przypadkach i zachować dużą staranność podczas pisania kodu. Ponadto, jeśli projekt wymaga funkcji współdzielenia, może być konieczne zapakowanie ich w postać std::shared_ptr. A jeśli muszą być kopiowane i mogą być stanowe, staje się jeszcze gorsze.

Bez względu na to, jak postrzegasz podejście nr 3, to on został ustandaryzowany. Ale w świetle wyżej wymienionych problemów łatwo można się także bronić.

W rzeczywistości napisałem szablon klasy unique_function, który wykorzystuje podejście nr 1 do mojego obecnego projektu, ponieważ dla nas przypadek użycia przechowywania niekopiowych asynchronicznych obiektów zadań jest dość powszechny, a kopiowanie lub udostępnianie takiego zadanie nie jest konieczne.