Search code examples
c++multithreadingc++14variadic-templatesgeneric-lambda

Forward variadic argument to thread with lambda


I'm having trouble finding how use std::thread() with lambdas. Namely, with having a variadic argument lambda receive the arguments by forwarding. As an example:

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: compiles

A.2: compiles

B.1: compiles

B.2: doesn't compile

I don't understand:

  • Why the std::thread() (2) version using (B) doesn't compile and (A.2) does?
  • Why are there differences between (B.1) and (B.2)

Solution

  • Try with

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

    I mean: in the lambda you pass to std::thread(), auto && ... instead of T && .... Or, maybe, T const & ....

    I'm not a language layer, and maybe someone can correct me, but it seems to me that there is a clash between universal references and r-value references. And the fact that std::thread() pass copies of the following arguments to the first one.

    When you write

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

    the && are universal-references and T... become int, when you call foo(1), and int &, when you call foo(n).

    Inside the function you get

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

    in case f(0).

    And this works because both lambda are waiting a int by copy and this ever works.

    But when you call f(n), inside foo() you get

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

    and this works for the first call, because the lambda wait a int left-reference variable (int &) and get a int left-reference variable, but doesn't works for the second call because std::thread pass a copy of std::forward<int>(t) (so a right-reference, int &&) to the lambda that wait for a left-reference.