丢弃限定符未知原因 (std::bind() / lambda)
Discards qualifiers unknown cause (std::bind() / lambda)
我不明白 where/why 限定词 被丢弃。
#include <iostream>
#include <memory>
class A {
public:
void f() {};
};
template <typename Callable>
void invoker(Callable c) {
auto l = [=]() {
c(); // <------------------- Error
};
l();
}
int main() {
A a;
invoker(std::bind(&A::f, a));
return 0;
}
我在 c(); 行遇到编译器错误:
error: passing ‘const std::_Bind(A)>’ as
‘this’ argument of ‘_Result std::_Bind<_Functor(_Bound_args
...)>::operator()(_Args&& ...) [with _Args = {}; _Result = void;
_Functor = std::_Mem_fn; _Bound_args = {A}]’ discards qualifiers [-fpermissive]
c();
我不明白的解决方法:
定义 A::f() 为常量:void f() const {};
bind()
对实例的引用 a
: invoker(std::bind(&A::f, std ::ref(a)));。
通过ref
传递给lambda
:auto l = [&]() {
g++ 版本:
g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
根据this:
The return type of std::bind
holds a member object of type std::decay<F>::type
constructed from std::forward<F>(f)
, and one object per each of args...
, of type std::decay<Arg_i>::type
, similarly constructed from std::forward<Arg_i>(arg_i)
.
这意味着 c
拥有 a
的副本(我们称之为 c.a
),但是由于您通过副本捕获 c
,因此 c
在 lambda 内部是 const
限定的,并且 c.a
"inherits" 在调用 &A::f
时 c
的常量(参见本答案的末尾)。
- 使
f()
成为 const
成员函数允许您在 const
对象上调用它。
- 如果您使用
std::ref(a)
,c
不会保留 a
的副本,而是 std::reference_wrapper<A>
,并且 operator()
的工作方式适用于 std::reference_wrapper<A>
不同(见本答案结尾)。
- 当通过引用捕获
[&]
时,c
不再是 const
,因此您可以在 c.a
.[=94= 上调用非常量成员函数]
额外的细节(仍然来自 this):
If the stored argument arg
is of type std::reference_wrapper<T>
(for example, std::ref
or std::cref
was used in the initial call to bind
), then the argument vn
in the std::invoke
call above is arg.get()
and the type Vn
in the same call is T&
: the stored argument is passed by reference into the invoked function object.
所以调用 std::ref(a)
等同于:
std::invoke(&A::f, std::forward<A&>(c_a.get()));
其中 c_a
是存储在 c
中的 std::reference_wrapper<A>
。注意转发类型是A&
,不是A const&
.
Otherwise, the ordinary stored argument arg
is passed to the invokable object as lvalue argument: the argument vn
in the std::invoke
call above is simply arg
and the corresponding type Vn
is T cv &
, where cv
is the same cv-qualification as that of g
.
所以原来的调用等价于(因为c
是const,所以cv
是const
):
std::invoke(&A::f, std::forward<A const&>(c_a));
其中 c_a
是 c
中 A
的副本。
霍尔特回答得很好。
如果您无法将 A::f 修改为 const 方法,您可以使 lambda 可变:
#include <iostream>
#include <memory>
#include <functional>
class A {
public:
void f() {};
};
template <typename Callable>
void invoker(Callable c) {
auto l = [=]() mutable // <-- Fixed
{
c();
};
l();
}
int main() {
A a;
invoker(std::bind(&A::f, a));
return 0;
}
我不明白 where/why 限定词 被丢弃。
#include <iostream>
#include <memory>
class A {
public:
void f() {};
};
template <typename Callable>
void invoker(Callable c) {
auto l = [=]() {
c(); // <------------------- Error
};
l();
}
int main() {
A a;
invoker(std::bind(&A::f, a));
return 0;
}
我在 c(); 行遇到编译器错误:
error: passing ‘const std::_Bind(A)>’ as ‘this’ argument of ‘_Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {}; _Result = void; _Functor = std::_Mem_fn; _Bound_args = {A}]’ discards qualifiers [-fpermissive] c();
我不明白的解决方法:
定义 A::f() 为常量:void f() const {};
bind()
对实例的引用a
: invoker(std::bind(&A::f, std ::ref(a)));。通过
ref
传递给lambda
:auto l = [&]() {
g++ 版本:
g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
根据this:
The return type of
std::bind
holds a member object of typestd::decay<F>::type
constructed fromstd::forward<F>(f)
, and one object per each ofargs...
, of typestd::decay<Arg_i>::type
, similarly constructed fromstd::forward<Arg_i>(arg_i)
.
这意味着 c
拥有 a
的副本(我们称之为 c.a
),但是由于您通过副本捕获 c
,因此 c
在 lambda 内部是 const
限定的,并且 c.a
"inherits" 在调用 &A::f
时 c
的常量(参见本答案的末尾)。
- 使
f()
成为const
成员函数允许您在const
对象上调用它。 - 如果您使用
std::ref(a)
,c
不会保留a
的副本,而是std::reference_wrapper<A>
,并且operator()
的工作方式适用于std::reference_wrapper<A>
不同(见本答案结尾)。 - 当通过引用捕获
[&]
时,c
不再是const
,因此您可以在c.a
.[=94= 上调用非常量成员函数]
额外的细节(仍然来自 this):
If the stored argument
arg
is of typestd::reference_wrapper<T>
(for example,std::ref
orstd::cref
was used in the initial call tobind
), then the argumentvn
in thestd::invoke
call above isarg.get()
and the typeVn
in the same call isT&
: the stored argument is passed by reference into the invoked function object.
所以调用 std::ref(a)
等同于:
std::invoke(&A::f, std::forward<A&>(c_a.get()));
其中 c_a
是存储在 c
中的 std::reference_wrapper<A>
。注意转发类型是A&
,不是A const&
.
Otherwise, the ordinary stored argument
arg
is passed to the invokable object as lvalue argument: the argumentvn
in thestd::invoke
call above is simplyarg
and the corresponding typeVn
isT cv &
, wherecv
is the same cv-qualification as that ofg
.
所以原来的调用等价于(因为c
是const,所以cv
是const
):
std::invoke(&A::f, std::forward<A const&>(c_a));
其中 c_a
是 c
中 A
的副本。
霍尔特回答得很好。
如果您无法将 A::f 修改为 const 方法,您可以使 lambda 可变:
#include <iostream>
#include <memory>
#include <functional>
class A {
public:
void f() {};
};
template <typename Callable>
void invoker(Callable c) {
auto l = [=]() mutable // <-- Fixed
{
c();
};
l();
}
int main() {
A a;
invoker(std::bind(&A::f, a));
return 0;
}