我很惊讶地发现此代码可以编译:
#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正确性?
答案 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}}。正如预期的那样,由于该运算符被删除,您会得到一个错误。