user10579912
user10579912

Reputation: 111

std::function const correctness not followed

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

Answers (3)

joe_chip
joe_chip

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

user10580110
user10580110

Reputation: 41

You are correct to find this weird. The call operator of std::function is marked const but the constness 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

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385274

std::function adds a layer of indirection, and this layer of indirection does not pass through constness 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

Related Questions