没有匹配函数可以调用类型为'const std :: __ 1 :: packaged_task <void()=“”>'的对象,但是`std :: is_const`返回false

时间:2018-12-10 07:32:11

标签: c++

#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;
}

我知道错误的含义;我知道制作lambda mutable会有所帮助,我对此进行了测试,但是我想知道const的来源。请注意,std::is_const返回false,这让我感到非常困惑。

编辑: 抱歉忘了提起编译器。我正在使用clang-1000.10.44.2。该命令为clang++ -std=c++14 test.cpp -o test

1 个答案:

答案 0 :(得分:1)

我将假设您使用的是7.0版以上的Clang。 bug report 38325记录了此行为。但是,它也提供了为什么您看到的行为并非完全不合理的理由。引用错误报告中的Richard Smith:

  

每个[expr.prim.id.unqual] p2,lambda中的'x'或'y'类型   是命名成员的类成员访问表达式的类型   相应的捕获。这些成员是(non-const)int类型的,但是   lambda的*此参数是const限定的

要解释这一点,请记住,lambda表达式引入了唯一的闭包类型。就像你写的一样:

struct unique {
    decltype(task1) task2;
    void operator()() const { /* Your code */ }
};
unique const tmp = unique{std::move(tmp1)};

现在,虽然tmp 是const ,但标识符task2的“命名实体的类型”不具有const限定(成员类型没有cv限定) 。所以你有它。即使task2用作函数调用操作符左侧的 postfix-expression 时,它会保留const限定符,但在检查decltype(task2)时,您可能看不到。解决方法是强制将task2视为正则表达式,而不要遵守decltype对于id表达式的特殊规则。您可以通过添加括号来做到这一点:

std::is_const<std::remove_reference_t<decltype((task2))>>::value

decltype((task2))适用于(task2)的类型和值类别,即decltype(task1) const&remove_reference_t给了我们decltype(task1) const,您检查的谓词报告了您的期望。