8

我有一个接收函数对象的模板函数。有时函数对象是无状态结构,但有时它们是大型有状态对象。函数对象的状态在这个函数中没有改变,只是检查。我也非常热衷于编写编译器可以尽可能优化的代码。选择参数类型时应该考虑什么?

该函数属于这种类型:

template<typename funcT>
auto make_particle(funcT fun) {
   Particle<typename funcT::result_type> particle;
   particle = fun();
   return particle;
}

参数类型可能应该是funcT const & fun这样大对象不被复制,但是为什么大多数人使用按值调用函数对象呢?我是否通过使用 const 引用丢失了一些东西?或者我应该使用左值引用?请注意,c++1y 是可以的,上面的代码示例只是一个示例。

4

2 回答 2

8

有几个用例,它们都应该可用:

  • 函子没有状态,并作为临时提供:make_particle(MyFun())

  • 函子有一个需要稍后恢复的状态:YourFun f; make_particle(f);

你不能用一个引用类型参数来解决这两种情况:第一种情况需要一个 const 左值引用或一个右值引用,它禁止第二次使用,第二种情况需要一个左值引用,它禁止第一次使用。

在这种情况下,一个常见的习惯用法是按值接受函子,并在最后返回它

template <typename Iter, typename F>
F map(Iter first, Iter last, F f)
{
    // ... f(*first) ...
    return f;
}

不过,这可能并不完全适用于您的情况,但这是一个想法。例如,您可以返回一个std::pair<ParticleType, F>. 在任何情况下,您都需要您的仿函数类型是可复制的,但这是一个合理的要求。

@Xeo 有用地指出的另一种方法,仅适用于函数模板,是通过通用引用获取函子参数,这在两种情况下都适用:

template <typename Iter, typename F>
void map(Iter first, Iter last, F && f)
{
    // ... use f(*first) ...
}

请注意,在这种情况下,我们使用std::forward,因为我们将f其用作真正的参考,而不仅仅是将其传递给其他地方。f特别是,如果我们仍打算使用它,则不允许我们搬离。

于 2014-01-20T09:35:50.937 回答
5

参数类型可能应该是 funcT const & fun 以便不复制大对象,

这不是标准库中的算法所采取的观点。在那里,可调用对象按值获取。由可调用对象的作者来确保它的复制成本相当低廉。例如,如果它需要访问大的东西,那么你可以让函子的用户提供一个引用并将其存储在函子中——复制一个引用很便宜。

现在,您可能希望做一些与标准库不同的事情,因为您希望粒子生成函数很难以便宜的价格复制或移动。但是 C++ 程序员熟悉被复制的函子,所以如果你按照标准库所做的那样去做,那么通常你不会让他们的生活变得比以前更糟。复制函子不是问题,除非你把它变成一个:-)

于 2014-01-20T10:07:04.030 回答