简体   繁体   English

在C ++中传递函数

[英]Passing functions in C++

Suppose I want to write a function that calls a nullary function 100 times. 假设我想编写一个调用nullary函数100次的函数。 Which of these implementations is best and why? 哪些实现最好,为什么?

template<typename F>
void call100(F f) {
    for (int i = 0; i < 100; i++)
        f();
}

template<typename F>
void call100(F& f) {
    for (int i = 0; i < 100; i++)
        f();
}

template<typename F>
void call100(const F& f) {
    for (int i = 0; i < 100; i++)
        f();
}


template<typename F>
void call100(F&& f) {
    for (int i = 0; i < 100; i++)
        f();
}

Or is there a better implementation? 或者是否有更好的实施?

Update regarding 4 关于4的更新

struct S {
    S() {}
    S(const S&) = delete;
    void operator()() const {}
};

template<typename F>
void call100(F&& f) {
    for (int i = 0; i < 100; i++)
        f();
}

int main() {
    const S s;
    call100(s);
}

I would use the first one (pass the callable by value). 我会使用第一个(通过值传递callable)。

If a caller is concerned about the cost of copying the callable, then they can use std::ref(f) or std::cref(f) to pass it using reference_wrapper . 如果调用者担心复制可调用的代价,那么他们可以使用std::ref(f)std::cref(f)来使用reference_wrapper传递它。

By doing this, you provide the most flexibility to the caller. 通过这样做,您可以为调用者提供最大的灵活性。

The only runtime cost of 唯一的运行时成本

template<typename F>
void call100(F&& f) {
  for (int i = 0; i < 100; ++i)
    f();
}

is that it can have more versions (copies of code) if you pass f in multiple ways. 如果你以多种方式传递f ,它可以拥有更多版本(代码副本)。 With MSVC or the gold linker with ICF, those copies only cost compile time unless they differ, and if they differ you probably want to keep them. 使用MSVC或带有ICF的黄金链接器,这些副本只需要编译时间,除非它们不同,如​​果它们不同,您可能希望保留它们。

template<typename F>
void call100(F f) {
  for (int i = 0; i < 100; ++i)
    f();
}

this one has the advantage of being value semantics; 这个具有价值语义的优势; and following the rule of taking values unless you have good reason not to is reasonable. 除非你有充分的理由不合理,否则遵守取值的规则。 std::ref / std::cref let you call it with a persistant reference, and for prvalues guaranteed elision will prevent a spurious copy. std::ref / std::cref允许你使用持久引用来调用它,对于prvalues 保证elision将防止虚假副本。

As a joke you could do: 作为一个笑话,你可以这样做:

template<typename F>
void call100(F&& f) {
  for (int i = 0; i < 99; ++i)
    f();
  std::forward<F>(f)();
}

but that relies on people having && overloads on their operator() , which nobody does. 但这取决于人们在operator()上有&&重载,没有人这样做。

I do not think there is a definitive answer: 我认为没有明确的答案:

  1. The first one copies everything you pass in which might be expensive for capturing lambdas but otherwise provides the most flexibility: 第一个复制您传递的所有内容,这对于捕获lambda可能是昂贵的,但另外提供了最大的灵活性:

    Pros 优点

    • Const objects allowed 允许使用Const对象
    • Mutable objects allowed (copied) 允许可变对象(复制)
    • Copy can be elided (?) 复制可以省略(?)

    Cons 缺点

    • Copies everything you give it 复制您提供的所有内容
    • You cannot call it with an existing object such as mutable lambda without copying it in 您不能使用现有对象(如mutable lambda)调用它而不复制它
  2. The second one cannot be used for const objects. 第二个不能用于const对象。 On the other hand it does not copy anything and allows mutable objects: 另一方面,它不复制任何东西并允许可变对象:

    Pros 优点

    • Mutable objects allowed 允许可变对象
    • Copies nothing 没有复制

    Cons 缺点

    • Does not allow const objects 不允许const对象
  3. The third one cannot be used for mutable lambdas so is a slight modification of the second one. 第三个不能用于可变lambdas,所以是对第二个的略微修改。

    Pros 优点

    • Const objects allowed 允许使用Const对象
    • Copies nothing 没有复制

    Cons 缺点

    • Cannot be called with mutable objects 无法使用可变对象调用
  4. The fourth one cannot be called with const objects unless you copy them which becomes quite awkward with lambdas. 第四个不能用const对象调用,除非你复制它们使用lambdas变得非常尴尬。 You also cannot use it with pre-existing mutable lambda object without copying it or moving from it (losing it in the process) which is similar limitation to 1. 您也不能将它与预先存在的可变lambda对象一起使用而不复制或移动它(在此过程中丢失它),这类似于1的限制。

    Pros 优点

    • Avoids copies explicitely by forcing (requiring) move semanthics if the copy is needed 如果需要副本,则强制(要求)移动semanthics,明确避免复制
    • Mutable objects allowed. 允许可变对象。
    • Const objects allowed (except for mutable lambdas) 允许使用Const对象(可变lambda除外)

    Cons 缺点

    • Does not allow const mutable lambdas without a copy 不允许const可变lambda没有副本
    • You cannot call it with an existing object such as mutable lambda 你不能用现有的对象如mutable lambda来调用它

And there you have it. 你有它。 There is no silver bullet here and there are different pros & cons to each of these versions. 这里没有银弹,每个版本都有不同的优缺点。 I tend to lean towards the first one being the default but with certain types of capturing lambdas or bigger callables, it might become an issue. 我倾向于倾向于第一个是默认值但是使用某些类型捕获lambda或更大的callables,它可能会成为一个问题。 And you cannot call the 1) with the mutable object and get an expected result. 并且您不能使用可变对象调用1)并获得预期结果。 As mentioned in the other answer some of these can be overcome with std::ref and other ways of manipulating the actual T type. 正如在另一个答案中提到的,其中一些可以用std::ref和其他操纵实际T类型的方法来克服。 In my experience, these tend to be the source of pretty nasty bugs though when T is then something different than one expects to achieve ie mutability of a copy or such. 根据我的经验,这些往往是相当令人讨厌的错误的来源,尽管当时T是不同于预期实现的东西,即副本的可变性等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM