我在推理如何编写和设计几个应该处理特定文件格式的函数,这些函数可能有不同的实现和不同的版本,每个函数需要一种不同的方法来解码文件中的这些信息。 / p>
我像往常一样浏览标准库,并得到std::function
存在的余数,但问题是我无法弄清楚为什么我可能对使用std::function
感兴趣,在C和C ++中编程的第一条规则之一是,如果你不必引用你不一定要命名的东西,你可以获得未命名的数据结构和未命名的/ lambda函数,但通常是函数有一个名字,就我记忆而言,lambda的类型仍然是实现定义的,那么对std::function
的需求是什么?
例如在我的情况下,我考虑使用map
或hash table
(但所涉及的函数数量实际上很小,现在最多2-3个),其中一对由tag
(代表文件格式的版本/实现)+ functions
,我想知道为什么我不能只使用std::string
和函数指针作为每对的2种类型;我也无法理解为什么我们在标准库中有std::function
。
我的意思是,当你需要一个未命名的函数并且你需要一个对象来描述它的状态时呢?
答案 0 :(得分:5)
这个问题已经多次得到回答,答案来自不同的观点。
(按升序问题编号排序。)
我的猜测是std::function
可以使lambda
易于使用。
基本上,当你需要完成小事时,仿函数会更方便。当事物超越"小"时,接口(抽象基类,或C ++中的ABC)更适合。规模。
更具体地说:
std::function
和工具。 std::bind
以及std::function
和lambda。std::function
签名,跳过#2和#3,将#4放在方括号内的逗号分隔列表中,并将#5放在花括号内。大力使用auto
。超出"单行" 或"一个回调函数" 的任何内容都需要一个界面(ABC)
此外,如果您的代码库未编译为单个部分(即库和链接的分离),则库上公开的任何回调函数最好需要使用接口(ABC)。根据您的编译器和链接方法,这可能是也可能不是问题。
答案 1 :(得分:5)
std::function
基于较早的boost::function
,documentation很好,引言说:
通常,在函数指针用于推迟调用或进行回调的任何地方,可以使用Boost.Function来允许用户在目标实现方面具有更大的灵活性。目标可以是任何“兼容”的函数对象(或函数指针),这意味着Boost.Function指定的接口的参数可以转换为目标函数对象的参数。
要添加std::tr1::function
的{{3}}也有一节描述该功能的动机。它被添加到标准中,因为数千人发现boost::function
多年有用。
如果您仍然不明白为什么它有用,您可能无法理解函数指针或回调在哪里有用,所以请查看。
答案 2 :(得分:3)
std::function<R(Args...)>
类型删除 copy , destroy 和使用Args调用...并返回R ,并实现移动
从基本回调开始 - R(*)(Args...)
,主叫方提供Args...
。现在一个好的回调有一个void*
你传递给调用者,然后他们回传 - 现在你有R(*)(void*,Args...)
。接下来,我们需要重新分发和回收void*
回调数据 - 因此我们有void*
,R(*)(void*,Args...)
和void(*)(void*)
清理。接下来,我们需要值语义 - 所以调用者也传递如何克隆 void*
- 一个void*(*)(void*)
克隆函数(值语义非常棒,真的)。
现在你有一个std::function<R(Args...)>
的C风格版本。 std::function
在构造函数中自动生成上述内容,典型的实现将接口存储到抽象类中,该抽象类为传入的可调用类型实现上述内容。
因此,类型擦除可让您获取支持上述类型擦除概念的任何值,并将它们存储在统一类型中 - std::function<R(Args...)>
。
这允许您接受符合您需求的任何内容,并统一与之交互。您可以将它存储在公共类中,您可以将其传递给动态加载的库,或者将其传递给不直接依赖于函数和状态的原始选择的代码块。
你可以使用C风格的版本,但它包装起来很尴尬。
std::function
的变体会消除 copy 概念,而有些则会消除 destroy (即不拥有),如即使没有抽象调用也是值得的。