Reputation: 3
#include <functional>
#include <future>
#include <iostream>
#include <thread>
template<class Func, class... Args>
void submit(Func&& f, Args&&... args) {
using returnType = typename std::result_of<Func(Args...)>::type;
auto task1 = std::packaged_task<returnType()>(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
std::cout << std::is_const<decltype(task1)>::value << " task1 const or not" << std::endl;
auto tmp = [task2=std::move(task1)]() {
std::cout << std::is_const<decltype(task2)>::value << " const or not" << std::endl; // print 0, which means non-const
// task2(); // uncomment this line, compilation will fail
};
tmp();
}
int main() {
submit([&] {
std::cout << "fooooooooo" << std::endl;
});
return 0;
}
I know the meaning of error; I know making lambda mutable
will help and I test it, but I want to know where does the const
come from. Note that std::is_const
returns false, which makes me really confused.
EDIT:
Sorry forgot to mention the compiler. I was using clang-1000.10.44.2. The command is clang++ -std=c++14 test.cpp -o test
Upvotes: 0
Views: 1823
Reputation: 170084
I'm going to assume you are using Clang up to version 7.0. This behavior is documented by bug report 38325. However, it also presents rationale why the behavior you see is not totally unreasonable. To quote Richard Smith from the bug report:
Per [expr.prim.id.unqual]p2, the type of 'x' or 'y' within the lambda is the type of a class member access expression naming the corresponding capture. Those members are of type (non-const) int, but the lambda's *this parameter is const-qualified
To explain it, recall that a lambda expression introduces a unique closure type. It's as if you wrote:
struct unique {
decltype(task1) task2;
void operator()() const { /* Your code */ }
};
unique const tmp = unique{std::move(tmp1)};
Now while tmp
is const, the "type of the entity named" by the identifier task2
is not const qualified (the member type has no cv-qualifications). So there you have it. Even though task2
when used as a postfix-expression to the left of a function call operator preserves const qualifications, you may not see that when inspecting decltype(task2)
. The workaround is to force task2
to be treated as a regular expression, not subject to the special rules of decltype
for id-expressions. You do that by adding parentheses:
std::is_const<std::remove_reference_t<decltype((task2))>>::value
decltype((task2))
will be appropriate to the type and value category of (task2)
, which is decltype(task1) const&
. remove_reference_t
gives us decltype(task1) const
and the predicate you check reports what you would expect.
Upvotes: 1