Reputation: 111
I was surprised to find this code compiles:
#include <functional>
struct Callable {
void operator() () { count++; }
void operator() () const = delete;
int count = 0;
};
int main() {
const Callable counter;
// counter(); //error: use of deleted function 'void Callable::operator()() const'
std::function<void(void)> f = counter;
f();
const auto cf = f;
cf();
}
https://wandbox.org/permlink/FH3PoiYewklxmiXl
This will call the non-const call operator of Callable
. Comparatively, if you do const auto cf = counter; cf();
then it errors out as expected. So, why does const correctness not seem to be followed with std::function
?
Upvotes: 11
Views: 4518
Reputation: 2558
The reason is that assigning counter
to std::function
object creates a copy of counter
.
In your case, f
is initialized using the following constructor:
template< class F >
function( F f );
As described here, this constructor "initializes the target with std::move(f)" - a new object of type Callable
is created and initialized using copy constructor.
If you'd like to initialize f
with a reference to counter
instead, you can use std::ref
:
std::function<void()> f = std::ref(counter);
std::ref
returns an instance of std::reference_wrapper
, which has operator ()
, which calls Callable
's operator () const
. As expected, you'll get an error, since that operator is deleted.
Upvotes: 2
Reputation: 41
You are correct to find this weird. The call operator of std::function
is marked const
but the const
ness is not propagated when the target object is actually invoked. The proposal p0045r1 seems to remedy this by making the call operator of std::function
non-const but allowing the following syntax:
std::function<return_type(arg_type) const>
Upvotes: 4
Reputation: 385274
std::function
adds a layer of indirection, and this layer of indirection does not pass through const
ness to the callable.
I'm not really sure why this is — probably because std::function
takes a copy of the callable and has no need to keep the copy const
(in fact this might break assignment semantics) — I'm also not really sure why you'd need it to.
(Of course, directly invoking operator()
on an object of a type that you just so happened to call Callable
and declared as const
will require a const
context, as it would with any other object.)
Best practice is to give the callable a const
operator()
and leave it at that.
tl;dr: true, but not a bug, and doesn't matter
Upvotes: 5