Intel compiler failing to compile variadic lambda capture with multiple arguments

226 views Asked by At

Here is a the minimal example I was able to come up with:

#include <utility>
template<class CB, class... ARGS>
void call_lam(CB&& cb, ARGS&&... args) {
     auto lam = [&args...](auto&& callee) { 
                callee(std::forward<ARGS>(args)...);
     };
     lam(cb);
}

void exec(unsigned, int);

void foo() {
  unsigned x = 25;
  int y = 0;
  call_lam(exec, x, y);
}

Above example compiles with both CLang and gcc, but failes with icc 17.0 (as https://godbolt.org/g/tzMY6K shows). The error is as following:

/usr/include/c++/5/bits/move.h(89): error: static assertion failed with "template argument substituting _Tp is an lvalue reference type"

static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"

^ detected during:

instantiation of "_Tp &&std::forward<_Tp>(std::remove_reference<_Tp>::type &&) [with _Tp=unsigned int &]" at line 5 of ""

instantiation of function "lambda [](auto &&)->auto [with =void (&)(unsigned int, int)]" at line 7 of ""

instantiation of "void call_lam(CB &&, ARGS &&...) [with CB=void (&)(unsigned int, int), ARGS=]" at line 15 of "" compilation aborted for (code 2)

Compiler exited with result code 2

Playing with this example, I have found out that:

  • It has to be to distinct types as arguments to exec. When using two ints or a single argument, the error goes away
  • by replacing the types with something else (for example, std::string) and changing manner of passing arguments to exec (const&) the error can be reported as something else, saying that "overload for std::move can not be matched". At this point, it would also fail on icc16. This is slightly modified code: https://godbolt.org/g/qHrU6P

Provided the code is well-formed (as I believe it is), other than replacing lambda with a custom functor (which I do not want to do, as I do not want to capture variable number of arguments with proper references manually through tuples) does anybody see any workarounds here?

1

There are 1 answers

0
W.F. On

I think below code will meet your workaround requirements:

#include <utility>
#include <iostream>
#include <tuple>

template<class CB, class... ARGS>
void call_lam(CB&& cb, ARGS&&... args) {
     auto lam = [](auto&&... args2){
         return [&args2...](auto&& callee) { 
                callee(std::forward<std::tuple_element_t<0, std::decay_t<decltype(args2)>>>(std::get<0>(args2))...);
         };}(std::forward_as_tuple(std::forward<ARGS>(args))...);
     lam(cb);
}

struct A {
    A() {
        std::cout << "A()" << std::endl;
    }
    A(const A&) {
        std::cout << "A(const A&)" << std::endl;
    }
    A(A&&) {
        std::cout << "A(A&&)" << std::endl;
    }
};

void exec(A &, A) {
}

int main() {
  A a;
  call_lam(exec, a, A());
}