简体   繁体   English

没有遵循std :: function const的正确性

[英]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 https://wandbox.org/permlink/FH3PoiYewklxmiXl

This will call the non-const call operator of Callable . 这将调用Callable的非const调用操作符。 Comparatively, if you do const auto cf = counter; cf(); 相比之下,如果你做const auto cf = counter; cf(); const auto cf = counter; cf(); then it errors out as expected. 然后它按预期错误输出。 So, why does const correctness not seem to be followed with std::function ? 那么,为什么const正确性似乎没有跟随std::function

std::function adds a layer of indirection, and this layer of indirection does not pass through const ness to the callable. std::function添加了一个间接层,这个间接层不会通过const传递给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. 我不确定为什么会这样 - 可能是因为std::function需要一个可调用的副本而且不需要保留副本const (实际上这可能会破坏赋值语义) - 我也不确定你为什么这么做我需要它。

(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.) (当然,直接在一个类型的对象上调用operator() ,你恰好调用Callable并声明为const将需要一个const上下文,就像对任何其他对象一样。)

Best practice is to give the callable a const operator() and leave it at that. 最佳实践是给callable一个const operator()并保留它。

tl;dr: true, but not a bug, and doesn't matter tl; dr:是的,但不是一个bug,并不重要

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. std::function的调用运算符标记为const但实际调用目标对象时不会传播const The proposal p0045r1 seems to remedy this by making the call operator of std::function non-const but allowing the following syntax: 提议p0045r1似乎通过使std::function非const的调用运算符但允许以下语法来解决这个问题:

std::function<return_type(arg_type) const>

The reason is that assigning counter to std::function object creates a copy of counter . 原因是为std::function对象分配counter会创建一个counter的副本。

In your case, f is initialized using the following constructor: 在您的情况下,使用以下构造函数初始化f

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. 如上所述这里 ,这个构造“与标准::移动(F)初始化目标” -类型的新对象Callable创建和使用拷贝构造函数初始化。

If you'd like to initialize f with a reference to counter instead, you can use std::ref : 如果您想使用对counter的引用来初始化f ,则可以使用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 . std::ref返回一个std::reference_wrapper的实例,它有operator () ,它调用Callableoperator () const As expected, you'll get an error, since that operator is deleted. 正如预期的那样,您将收到错误,因为该运算符已被删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM