Programing

std :: function을 복사해야합니까? 아니면 항상 참조 할 수 있습니까?

lottogame 2020. 12. 9. 07:39
반응형

std :: function을 복사해야합니까? 아니면 항상 참조 할 수 있습니까?


내 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?
   };

보시다시피 생성자에서 함수 인수를 참조로 추가했습니다.

그러나 내 클래스에 함수를 저장하는 가장 좋은 방법은 무엇입니까?

  • std :: function은 함수 포인터 일 뿐이며 함수의 '실행 가능한 코드'가 메모리에 유지되도록 보장되므로 함수를 참조로 저장할 수 있습니까?
  • 람다가 전달되고 호출자가 돌아올 경우를 대비하여 복사본을 만들어야합니까?

내 직감은 참조를 저장하는 것이 안전하다고 말합니다. 컴파일러가 컴파일 시간에 람다에 대한 코드를 생성하고 응용 프로그램이 실행되는 동안이 실행 코드를 '가상'메모리에 보관할 것으로 예상합니다. 따라서 실행 코드는 '삭제'되지 않으며 이에 대한 참조를 안전하게 저장할 수 있습니다. 그러나 이것이 정말로 사실입니까?


std :: function은 함수 포인터 일 뿐이며 함수의 '실행 가능한 코드'가 메모리에 유지되도록 보장되므로 함수를 참조로 저장할 수 있습니까?

std::function함수 포인터가 아닙니다. 임의의 호출 가능 객체를 감싸는 래퍼이며 해당 객체를 저장하는 데 사용되는 메모리를 관리합니다. 다른 유형과 마찬가지로 참조가 사용될 때마다 참조 된 객체가 여전히 유효 함을 보장하는 다른 방법이있는 경우에만 참조를 저장하는 것이 안전합니다 .

참조를 저장해야 할 합당한 이유와 유효 함을 보장하는 방법이 없다면 값별로 저장하십시오.

const생성자 대한 참조로 전달하는 것이 안전하며 값을 전달하는 것보다 더 효율적일 수 있습니다. const참조가 아닌 전달은 임시 전달을 방지하므로 사용자가 람다,의 결과 bind또는 std::function<int(int)>자신을 제외한 다른 호출 가능한 객체를 직접 전달할 수 없기 때문에 나쁜 생각 입니다.


함수를 참조로 생성자에 전달하고 복사본을 만들지 않으면 참조가 더 이상 유효하지 않으므로 함수가이 개체의 범위를 벗어날 때 운이 좋지 않습니다. 이미 이전 답변에서 많이 언급되었습니다.

내가 추가하고 싶었던 것은 대신 참조가 아닌 value 로 생성자에 함수를 전달할 수 있다는 것 입니다. 왜? 음, 어쨌든 복사본이 필요하므로 값으로 전달하면 컴파일러가 임시가 전달 될 때 복사본을 만들어야하는 필요성을 최적화 할 수 있습니다 (예 : 제자리에 작성된 람다 식).

물론 어떤 일을하든 전달 된 함수를 변수에 할당 할 때 잠재적으로 다른 복사본을 만들 수 있으므로를 사용 std::move하여 해당 복사본을 제거하십시오. 예:

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

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

private:
   MyFunction m_myFunction;
};

따라서 위의 rvalue를 전달하면 컴파일러는 첫 번째 복사본을 생성자로 최적화하고 std :: move는 두 번째 복사본을 제거합니다. :)

당신의 (만) 생성자는 const를 참조를 취하면 됩니다 관계없이 전달 어떻게의 기능에의 복사본을 만들 필요가있다.

대안은 lvalue와 rvalue를 개별적으로 처리하기 위해 두 개의 생성자를 정의하는 것입니다.

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;
};

이제 rvalue를 다르게 처리하고 명시 적으로 처리하여 (컴파일러가 처리하도록하는 대신)이 경우 복사 할 필요가 없습니다. 첫 번째 것보다 약간 더 효율적일 수 있지만 코드도 더 많습니다.

관련 참조 (그리고 아주 좋은 읽기) : http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


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.


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.


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.

참고URL : https://stackoverflow.com/questions/8711391/should-i-copy-an-stdfunction-or-can-i-always-take-a-reference-to-it

반응형