std :: function const正确性未遵循

时间:2018-10-30 10:49:53

标签: c++ const std-function

我很惊讶地发现此代码可以编译:

#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

这将调用Callable的非常量调用运算符。相比较而言,如果您执行const auto cf = counter; cf();,则它会按预期方式出错。那么,为什么std::function似乎没有遵循const正确性?

3 个答案:

答案 0 :(得分:5)

std::function添加了一个间接层,并且该间接层并不通过const形式传递给可调用对象。

我不太确定为什么会这样-可能是因为std::function提取了可调用对象的副本,而不必保留副本const(实际上这可能会破坏赋值语义)–我我也不太确定为什么要这么做。

(当然,直接在您恰巧调用operator()并声明为Callable的类型的对象上调用const会需要一个const上下文,就像其他任何对象一样。)

最佳做法是给可调用对象一个const operator(),然后将其保留。

tl; dr:是,但不是错误,没关系

答案 1 :(得分:1)

您发现这很奇怪。 std::function的调用运算符被标记为const,但是在实际调用目标对象时不会传播const。提案p0045r1似乎通过将std::function的调用运算符设为非常量但允许以下语法来弥补这一点:

std::function<return_type(arg_type) const>

答案 2 :(得分:0)

原因是将counter分配给std::function对象会创建counter的副本。

对于您而言,f使用以下构造函数初始化:

template< class F >
function( F f );

here所述,此构造函数“使用std :: move(f)初始化目标” -创建并使用copy初始化类型为Callable的新对象构造函数。

如果您想使用对f的引用来初始化counter,则可以使用std::ref

std::function<void()> f = std::ref(counter);

std::ref返回std::reference_wrapper的实例,该实例具有operator (),该实例调用Callable的{​​{1}}。正如预期的那样,由于该运算符被删除,您会得到一个错误。