如何实现std :: function? [英] How is std::function implemented?
问题描述
std :: function
应具有固定大小,但它必须能够包装任何类型的可调用项,包括任何同类型的lambda。如何实现?如果 std :: function
在内部使用指向其目标的指针,那么当 std :: function
实例是复制还是移动?是否有任何堆分配?
std :: function
可以从一个实现到另一个不同,但核心思想是它使用类型擦除。虽然有多种方法可以做,你可以想象一个平凡的(不是最优的)解决方案可能是这样的(简化为 std :: function< int(double)>
为了简单起见):
struct callable_base {
virtual int operator )= 0;
virtual〜callable_base(){}
};
template< typename F>
struct callable:callable_base {
F functor;
callable(F functor):functor(functor){}
virtual int operator()(double d){return functor(d); }
};
class function_int_double {
std :: unique_ptr< callable_base> C;
public:
template< typename F>
function(F f){
c.reset(new callable F(f));
}
int operator()(double d){return c(d); }
// ...
};
在这个简单的方法中, function
只将一个 unique_ptr
存储到基本类型。对于与函数
一起使用的每个不同函子,将创建从基本派生的新类型,并动态实例化该类型的对象。 std :: function
对象总是具有相同的大小,并将根据堆中不同的函子的需要分配空间。
在现实生活中,有不同的优化,提供性能优势,但复杂的答案。类型可以使用小对象优化,动态分派可以由一个自由函数指针替换,它接受函子作为参数,以避免一个间接层次...但是想法基本上是一样的。
关于 std :: function
行为的副本问题,
// g ++ 4.8
int main(){
int value = 5;
typedef std :: function< void()>有趣
fun f1 = [=]()mutable {std :: cout<<值++< '\\\
'};
fun f2 = f1;
f1(); // prints 5
fun f3 = f1;
f2(); // prints 5
f3(); //打印6(第一次递增后复制)
}
测试表明 f2
获取可调用实体的副本,而不是引用。如果可调用实体由不同的 std :: function<>
对象共享,则程序的输出将是5,6,7。
According to the sources I have found, a lambda expression is essentially implemented by the compiler creating a class with overloaded function call operator and the referenced variables as members. This suggests that the size of lambda expressions varies, and given enough references variables that size can be arbitrarily large.
An std::function
should have a fixed size, but it must be able to wrap any kind of callables, including any lambdas of the same kind. How is it implemented? If std::function
internally uses a pointer to its target, then what happens, when the std::function
instance is copied or moved? Are there any heap allocations involved?
The implementation of std::function
can differ from one implementation to another, but the core idea is that it uses type-erasure. While there are multiple ways of doing it, you can imagine a trivial (not optimal) solution could be like this (simplified for the specific case of std::function<int (double)>
for the sake of simplicity):
struct callable_base {
virtual int operator()(double d) = 0;
virtual ~callable_base() {}
};
template <typename F>
struct callable : callable_base {
F functor;
callable(F functor) : functor(functor) {}
virtual int operator()(double d) { return functor(d); }
};
class function_int_double {
std::unique_ptr<callable_base> c;
public:
template <typename F>
function(F f) {
c.reset(new callable<F>(f));
}
int operator()(double d) { return c(d); }
// ...
};
In this simple approach the function
object would store just a unique_ptr
to a base type. For each different functor used with the function
, a new type derived from the base is created and an object of that type instantiated dynamically. The std::function
object is always of the same size and will allocate space as needed for the different functors in the heap.
In real life there are different optimizations that provide performance advantages but would complicate the answer. The type could use small object optimizations, the dynamic dispatch can be replaced by a free-function pointer that takes the functor as argument to avoid one level of indirection... but the idea is basically the same.
Regarding the issue of how copies of the std::function
behave, a quick test indicates that copies of the internal callable object are done, rather than sharing the state.
// g++4.8
int main() {
int value = 5;
typedef std::function<void()> fun;
fun f1 = [=]() mutable { std::cout << value++ << '\n' };
fun f2 = f1;
f1(); // prints 5
fun f3 = f1;
f2(); // prints 5
f3(); // prints 6 (copy after first increment)
}
The test indicates that f2
gets a copy of the callable entity, rather than a reference. If the callable entity was shared by the different std::function<>
objects, the output of the program would have been 5, 6, 7.
这篇关于如何实现std :: function?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!