3

可能的重复:
模板通过值或常量引用传递或......?

对于将函数作为参数的函数,以下最佳做法是什么:

template<class Function> void test1(Function f);
template<class Function> void test2(Function& f);
template<class Function> void test3(const Function& f);

其中传递的函数可以是仿函数、std::function、函数指针或 lambda 函数。

4

2 回答 2

6

使用通用引用,您无需考虑它:

template<class Function> void test(Function&& f);
于 2012-11-20T22:32:18.660 回答
4

当参数被传递给推断参数类型的函数模板时,应该如何传递参数是一个有趣的问题。值得注意的是,使用参数的确切性质无关紧要:参数是否用作函数对象、迭代器、值等与答案无关。有四个选项:

  1. template <typename T> void f(T&& a)
  2. template <typename T> void f(T& a)
  3. template <typename T> void f(T const& a)
  4. template <typename T> void f(T a)

函数模板的实际作用有点重要:如果f()它的参数所做的a只是将其转发给另一个函数,那么您希望通过通用引用传递。被调用的函数将整理细节并拒绝不合适的选项。当然,虽然通常有用,但转发功能有些乏味。

如果f()真的对对象做了什么,我们通常可以立即丢弃两个选项:

  1. 通过通用引用传递会导致我们不知道它是引用还是值的类型。除了转发类型之外,这对于其他任何事情都毫无用处,因为它的行为要么像值,要么像引用。仅指定函数实际执行的操作将是一个有问题的舞蹈。
  2. 绕过对T const&我们也没有多大好处:我们得到了一个对象的引用,该对象的生命周期我们无法控制,我们既不能从中移动也不能复制/删除它。

如果对象本身被修改,则通过非const引用传递对象可能很有用。这显然是功能契约的一个重要部分,也是一个强加的设计选择,在采用时不能被功能的客户覆盖。

首选的方法是按值取参数。通常,这使得函数行为的定义变得更加容易,并且实际上为用户保留了类型应该遵循值还是引用语义的选择!仅仅因为某些东西是按值传递的,并不意味着感兴趣的对象也按值传递。特别是对于函数对象的情况,标准 C++ 库甚至提供了一个通用适配器,给出了值类型引用语义:std::ref().

当然,界面设计是微妙的,在某些情况下,每个选项都是必要的。但是,作为一般经验法则,我认为这很简单:

  1. 转发函数使用通用引用。
  2. 使用参数做某事的函数使用值。

...当然,这些规则仅适用于推导出类型的函数模板的参数。

于 2012-11-20T23:00:01.723 回答