std::函数常量正确性未遵循

std::function const correctness not followed

本文关键字:常量 函数 std 正确性      更新时间:2023-10-16

我很惊讶地发现这段代码编译:

#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

std::function添加了一层间接寻址,并且此间接层不会通过constness传递到可调用对象。

我不太确定为什么会这样——可能是因为std::function需要可调用的副本并且不需要保留副本const(事实上这可能会破坏赋值语义)——我也不确定为什么你需要它。

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

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

tl;DR:是的,但不是错误,也没关系

你觉得这很奇怪是正确的。std::function的调用运算符标记为const但在实际调用目标对象时不会传播const性。提案 p0045r1 似乎通过将调用运算符设置为非常量但允许以下语法来解决此问题std::function

std::function<return_type(arg_type) const>

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

在您的情况下,使用以下构造函数初始化f

template< class F >
function( F f );

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

如果要改为使用对counter的引用来初始化f,可以使用std::ref

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

std::ref返回一个std::reference_wrapper的实例,它有operator (),调用Callableoperator () const。正如预期的那样,您将收到一个错误,因为该运算符已删除。