Переслать вариационный аргумент в поток с лямбдой - PullRequest
0 голосов
/ 14 ноября 2018

У меня проблемы с поиском, как использовать std::thread() с лямбдами. А именно, имея аргумент с переменным значением, лямбда получает аргументы путем пересылки. Как пример:

template<typename... T> 
auto foo(T&&... t){
    [](T&&... t){}(std::forward<T>(t)...); // (1)

    return std::thread( // (2) 
        [](T&&... t){},
        std::forward<T>(t)...
    );
}

auto bar(){
    int n=1;
    foo(1); (A)
    foo(n); (B)
}

A.1: компилирует

A.2: компилирует

B.1: компилирует

B.2: не компилируется

Я не понимаю:

  • Почему std::thread() (2) версия, использующая (B), не компилируется, а (A.2) делает?
  • Почему существуют различия между (B.1) и (B.2)

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

std::thread не может просто пересылать свои аргументы в лямбду по ссылке, потому что это будет означать, что два потока потенциально имеют одновременный доступ к аргументам без синхронизации.Таким образом, вместо этого std::thread создает временные копии аргументов и передает их в лямбду.Потому что они временные, они ценности.Это работает в A.2, потому что лямбда-параметр является rvalue ссылкой (потому что T равно int, поэтому T&& равно int&&).В B.2 это не работает, потому что лямбда-параметр является ссылкой lvalue (потому что T равно int&, поэтому T&& равно int&).Как говорит max66, вы, вероятно, захотите использовать auto&&... в своей лямбде, чтобы она могла принимать все, что ей передано.

0 голосов
/ 14 ноября 2018

Попробуйте с

template<typename... T> 
auto foo(T&&... t){
    [](T&&... u){ }(std::forward<T>(t)...); // (1)

    return std::thread( // (2) 
        [](auto &&... u){ },
        std::forward<T>(t)...
    );
}

Я имею в виду: в лямбде вы переходите на std::thread(), auto && ... вместо T && ....Или, может быть, T const & ....

Я не являюсь языковым слоем, и, возможно, кто-то может исправить меня, но мне кажется, что существует конфликт между универсальными ссылками и ссылками на r-значения.И тот факт, что std::thread() передает копии следующих аргументов в первый аргумент.

Когда вы пишете

template<typename... T> 
auto foo(T&&... t)

, && являются универсальными ссылкамии T... становятся int, когда вы звоните foo(1), и int &, когда вы звоните foo(n).

Внутри функции вы получаете

[](int){ }(std::forward<int>(t)); // (1)

return std::thread( // (2) 
    [](int){ },
    std::forward<int>(t)...
);

в случаеf(0).

И это работает, потому что обе лямбды ждут копию int, и это всегда работает.

Но когда вы звоните f(n), внутри foo() вы получаете

[](int &){ }(std::forward<int>(t)); // (1)

return std::thread( // (2) 
    [](int &){ },
    std::forward<int>(t)...
);

, и это работает для первого вызова, потому что лямбда ожидает переменную левой ссылки int (int &) и получает переменную левой ссылки int, но не работает длявторой вызов, потому что std::thread передает копию из std::forward<int>(t) (так что ссылка справа int &&) в лямбду, ожидающую ссылку слева.

...