【发布时间】:2017-09-27 07:01:39
【问题描述】:
考虑以下代码:
#include <iostream>
#include <functional>
int main() {
auto run = [](auto&& f, auto&& arg) {
f(std::forward<decltype(arg)>(arg));
};
auto foo = [](int &x) {};
int var;
auto run_foo = std::bind(run, foo, var);
run_foo();
return 0;
}
使用 clang 编译时出现以下编译错误:
$ clang++ -std=c++14 my_test.cpp
my_test.cpp:6:9: error: no matching function for call to object of type 'const (lambda at my_test.cpp:8:16)'
f(std::forward<decltype(arg)>(arg));
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:998:14: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const (lambda at my_test.cpp:8:16) &, const int &>' requested here
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:1003:2: note: in instantiation of default argument for 'operator()<>' required here
operator()(_Args&&... __args) const
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
my_test.cpp:11:12: note: while substituting deduced template arguments into function template 'operator()' [with _Args = <>, _Result = (no value)]
run_foo();
^
my_test.cpp:8:16: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
auto foo = [](int &x) {};
^
my_test.cpp:8:16: note: conversion candidate of type 'void (*)(int &)'
1 error generated.
为什么arg 被推断为const int& 而不仅仅是int&?
std::bind documentation 说:
给定一个从较早的 bind 调用中获得的对象 g,当它是 在函数调用表达式 g(u1, u2, ... uM) 中调用,调用 存储对象的发生,就像通过 std::invoke(fd, std::forward(v1), std::forward(v2), ..., std::forward(vN)),其中 fd 是 std::decay_t 类型的值 确定绑定参数 v1、v2、...、vN 的值和类型 如下所示。
...
否则, 普通存储参数 arg 传递给可调用对象 左值参数:上面 std::invoke 调用中的参数 vn 是 简单的 arg 并且对应的类型 Vn 是 T cv &,其中 cv 是 与 g 相同的 cv 限定。
但在这种情况下,run_foo 是 cv-unqualified。我错过了什么?
【问题讨论】:
-
如果有帮助,
arg被推导出为int,但因为operator()是const,所以存储的var是const。 -
@Rakete1111 如果我没记错的话,
operator()有一个不合格的版本,当*this是const时会调用const版本,这就更奇怪了。 -
在我的实现中我只能看到
operator() const,但是是的,这很奇怪。 -
模板参数推导和替换发生在重载决议之前,所以即使
operator() const不会被重载决议选中,替换仍然发生。在这种情况下,替换失败不会出现在直接上下文中,因此这个替换失败是一个硬错误。 -
@cpplearner 从实现的角度来看这是一个很好的答案,但标准只是说
run_foo()的效果是run(foo, var),不是吗?我看不到它在哪里说“除非调用as_const(run)(as_const(foo), as_const(var))格式不正确”或类似的内容。