I don't understand where/why the qualifiers gets discarded.
#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;
}
I get a compiler error on the c(); line:
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();
Workarounds I don't understand:
Define A::f() as const: void f() const {};
bind()
a reference to instance a
: invoker(std::bind(&A::f, std::ref(a)));.
Pass to lambda
by ref
: auto l = [&]() {
g++ version:
g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
According to 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)
.
This means that c
holds a copy of a
(let's call it c.a
), but since you are capturing c
by copy, c
is const
-qualified inside the lambda, and c.a
"inherits" the constness of c
when invoking &A::f
(see end of this answer).
f()
a const
member function allows you to call it on a const
object.std::ref(a)
, c
does not hold a copy of a
but a std::reference_wrapper<A>
, and the way operator()
works for std::reference_wrapper<A>
is different (see end of this answer).[&]
, c
is not const
anymore, so you can call a non-const member function on c.a
.Extra details (still from 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.
So the call with std::ref(a)
is equivalent to:
std::invoke(&A::f, std::forward<A&>(c_a.get()));
Where c_a
is the std::reference_wrapper<A>
stored inside c
. Notice that the forward type is A&
, not 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
.
So the original call is equivalent to (because c
is const, so cv
is const
):
std::invoke(&A::f, std::forward<A const&>(c_a));
Where c_a
is the copy of A
inside c
.