C++ 我应该复制一个 std::function 还是我可以总是引用它?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8711391/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:01:43  来源:igfitidea点击:

Should I copy an std::function or can I always take a reference to it?

c++lambdastd-function

提问by Patrick

In my C++ application (using Visual Studio 2010), I need to store an std::function, like this:

在我的 C++ 应用程序(使用 Visual Studio 2010)中,我需要存储一个 std::function,如下所示:

class MyClass
   {
   public:
      typedef std::function<int(int)> MyFunction;
      MyClass (Myfunction &myFunction);
   private:
      MyFunction m_myFunction;    // Should I use this one?
      MyFunction &m_myFunction;   // Or should I use this one?
   };

As you can see, I added the function argument as a reference in the constructor.

如您所见,我在构造函数中添加了函数参数作为引用。

But, what is the best way to store the function in my class?

但是,在我的类中存储函数的最佳方法是什么?

  • Can I store the function as a reference since std::function is just a function-pointer and the 'executable code' of the function is guaranteed to stay in memory?
  • Do I have to make a copy in case a lambda is passed and the caller returns?
  • 由于 std::function 只是一个函数指针并且保证函数的“可执行代码”保留在内存中,我可以将该函数存储为引用吗?
  • 如果传递了 lambda 并且调用者返回,我是否必须进行复制?

My gut feeling says that it's safe to store a reference (even a const-reference). I expect the compiler to generate code for the lambda at compile time, and keep this executable code in 'virtual' memory while the application is running. Therefore the executable code is never 'deleted' and I can safely store a reference to it. But is this really true?

我的直觉是存储引用(甚至是常量引用)是安全的。我希望编译器在编译时为 lambda 生成代码,并在应用程序运行时将此可执行代码保存在“虚拟”内存中。因此,永远不会“删除”可执行代码,我可以安全地存储对它的引用。但这真的是真的吗?

回答by Mike Seymour

Can I store the function as a reference since std::function is just a function-pointer and the 'executable code' of the function is guaranteed to stay in memory?

由于 std::function 只是一个函数指针并且保证函数的“可执行代码”保留在内存中,我可以将该函数存储为引用吗?

std::functionis very much not just a function pointer. It's a wrapper around an arbitrary callable object, and manages the memory used to store that object. As with any other type, it's safe to store a reference only ifyou have some other way to guarantee that the referred object is still valid whenever that reference is used.

std::function不仅仅是一个函数指针。它是任意可调用对象的包装器,并管理用于存储该对象的内存。与任何其他类型一样,只有当您有其他方法来保证引用的对象在使用该引用时仍然有效时,可以安全地存储引用。

Unless you have a good reason for storing a reference, and a way to guarantee that it remains valid, store it by value.

除非你有一个很好的理由来存储一个引用,并且有一种方法来保证它保持有效,否则按值存储它。

Passing by constreference to the constructor is safe, and probably more efficient than passing a value. Passing by non-constreference is a bad idea, since it prevents you from passing a temporary, so the user can't directly pass a lambda, the result of bind, or any other callable object except std::function<int(int)>itself.

通过const引用传递给构造函数是安全的,并且可能比传递值更有效。通过非const引用传递是一个坏主意,因为它会阻止您传递临时对象,因此用户不能直接传递 lambda、 的结果bind或除std::function<int(int)>自身之外的任何其他可调用对象。

回答by jsdw

If you pass the function in to the constructor by reference, and don't make a copy of it, you'll be out of luck when the function goes out of scope outside of this object, as the reference will no longer be valid. That much has been said in the previous answers already.

如果您通过引用将函数传递给构造函数,并且不复制它,那么当函数超出此对象之外的范围时,您将不走运,因为引用将不再有效。前面的回答已经说了这么多。

What I wanted to add was that, instead, you could pass the function by value, not reference, into the constructor. Why? well, you need a copy of it anyway, so if you pass by value the compiler can optimize away the need to make a copy when a temporary is passed in (such as a lambda expression written in-place).

我想补充的是,相反,您可以通过而不是引用将函数传递给构造函数。为什么?好吧,无论如何您都需要它的副本,因此如果您按值传递,编译器可以优化在传入临时文件(例如就地编写的 lambda 表达式)时制作副本的需要。

Of course, however you do things, you potentially make another copy when you assign the passed in function to the variable, so use std::moveto eliminate that copy. Example:

当然,无论您做什么,当您将传入的函数分配给变量时,您可能会创建另一个副本,因此请使用std::move消除该副本。例子:

class MyClass
{
public:
   typedef std::function<int(int)> MyFunction;

   MyClass (Myfunction myFunction): m_myfunction(std::move(myFunction))
       {}

private:
   MyFunction m_myFunction;
};

So, if they pass in an rvalue to the above, the compiler optimises away the first copy into the constructor, and std::move removes the second one :)

因此,如果他们将右值传递给上面,编译器会将第一个副本优化到构造函数中,并且 std::move 删除第二个 :)

If your (only) constructor takes a const reference, you willneed to make a copy of it in the function regardless of how it's passed in.

如果您的(唯一)构造函数采用 const 引用,无论它是如何传入的,您需要在函数中复制它。

The alternative is to define two constructors, to deal with lvalues and rvalues separately:

另一种方法是定义两个构造函数,分别处理左值和右值:

class MyClass
{
public:
   typedef std::function<int(int)> MyFunction;

   //takes lvalue and copy constructs to local var:
   MyClass (const Myfunction & myFunction): m_myfunction(myFunction)
       {}
   //takes rvalue and move constructs local var:
   MyClass (MyFunction && myFunction): m_myFunction(std::move(myFunction))
       {}

private:
   MyFunction m_myFunction;
};

Now, you handly rvalues differently and eliminate the need to copy in that case by explicitly handling it (rather than letting the compiler handle it for you). May be marginally more efficient than the first but is also more code.

现在,您以不同的方式处理右值,并通过显式处理它(而不是让编译器为您处理它)消除了在这种情况下进行复制的需要。可能比第一个更有效,但代码也更多。

The (probably seen a fair bit around here) relevant reference (and a very good read): http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

(可能在这里看到了一些)相关参考(以及非常好的阅读):http: //cpp-next.com/archive/2009/08/want-speed-pass-by-value/

回答by Zack Yezek

As a general rule (especially if you're using these for some highly threaded system), pass by value. There is really no way to verify from within a thread that the underlying object is still around with a reference type, so you open yourself up to very nasty race and deadlock bugs.

作为一般规则(特别是如果您将这些用于某些高度线程化的系统),请按值传递。确实没有办法从线程内部验证底层对象是否仍然存在引用类型,因此您会遇到非常讨厌的竞争和死锁错误。

Another consideration is any hidden state variables in the std::function, for whom modification is very unlikely to be thread-safe. This means that even if the underlying function call is thread-safe, the std::function wrapper's "()" call around it MAY NOT BE. You can recover the desired behavior by always using thread-local copies of the std::function because they'll each have an isolated copy of the state variables.

另一个考虑因素是 std::function 中的任何隐藏状态变量,对它们的修改不太可能是线程安全的。这意味着即使底层函数调用是线程安全的,std::function 包装器的“()”调用也可能不是。您可以通过始终使用 std::function 的线程本地副本来恢复所需的行为,因为它们每个都有一个独立的状态变量副本。

回答by Nawaz

I would suggest you to make a copy:

我建议你复制一份:

MyFunction m_myFunction; //prefferd and safe!

It is safe because if the original object goes out of scope destructing itself, the copy will still exist in the class instance.

这是安全的,因为如果原始对象超出范围而自行销毁,则副本仍将存在于类实例中。

回答by Jan Hudec

Copy as much as you like. It is copyable. Most algorithms in standard library require that functors are.

随心所欲地复制。它是可复制的。标准库中的大多数算法都要求函子是。

However, passing by reference will probably be faster in non-trivial cases, so I'd suggest passing by constant reference and storing by value so you don't have to care about lifecycle management. So:

但是,在非平凡的情况下,通过引用传递可能会更快,因此我建议通过常量引用传递并按值存储,这样您就不必关心生命周期管理。所以:

class MyClass
{
public:
    typedef std::function<int(int)> MyFunction;
    MyClass (const Myfunction &myFunction);
          // ^^^^^ pass by CONSTANT reference.
private:
    MyFunction m_myFunction;    // Always store by value
};

By passing by constant or rvalue reference you promise the caller that you will not modify the function while you can still call it. This prevents you from modifying the function by mistake and doing it intentionally should usually be avoided, because it's less readable than using return value.

通过传递常量或右值引用,您向调用者保证在您仍然可以调用该函数时不会修改该函数。这可以防止您错误地修改函数,并且通常应该避免故意这样做,因为它不如使用返回值可读。

Edit:I originally said "CONSTANT or rvalue" above, but Dave's comment made me look it up and indeed rvalue reference does not accept lvalues.

编辑:我最初在上面说“常量或右值”,但戴夫的评论让我查了一下,确实右值引用不接受左值。