Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 269
user10579912 Avatar asked Oct 30 '18 10:10

user10579912


People also ask

How do you call a non-const function from a const function?

const member functions may be invoked for const and non-const objects. non-const member functions can only be invoked for non-const objects. If a non-const member function is invoked on a const object, it is a compiler error.

Can a const function return a non-const reference?

If the thing you are returning by reference is logically part of your this object, independent of whether it is physically embedded within your this object, then a const method needs to return by const reference or by value, but not by non-const reference.

How do you declare a constant in a function in C++?

Const member functions in C++ To make a member function constant, the keyword “const” is appended to the function prototype and also to the function definition header. Like member functions and member function arguments, the objects of a class can also be declared as const.

Why is std :: function slow?

If it is small, like 3-5 CPU instructions then yes std::function will make it slower, because std::function is not inlined into outer calling code. You should use only lambda and pass lambda as template parameter to other functions, lambdas are inlined into calling code.


2 Answers

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

like image 88
Lightness Races in Orbit Avatar answered Oct 14 '22 11:10

Lightness Races in Orbit


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>
like image 31
user10580110 Avatar answered Oct 14 '22 10:10

user10580110