Czy powinienem skopiować funkcję std::, Czy zawsze mogę odwołać się do niej?

W mojej aplikacji C++ (używającej Visual Studio 2010), muszę zapisać funkcję std::, tak:

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

Jak widzisz, dodałem argument funkcji jako odniesienie w konstruktorze.

Ale jaki jest najlepszy sposób na przechowywanie funkcji w mojej klasie?

  • Czy Mogę zapisać funkcję jako odniesienie, ponieważ std:: function jest tylko wskaźnikiem funkcji, a' kod wykonywalny ' funkcji jest gwarantowany, że pozostanie w pamięci?
  • Czy muszę zrobić kopię w przypadku lambda została przekazana, a dzwoniący wrócił?

Moje przeczucie mówi, że bezpieczne jest przechowywanie referencji (nawet const-reference). Oczekuję, że kompilator wygeneruje kod dla lambda w czasie kompilacji i zachowa ten kod wykonywalny w' wirtualnej ' pamięci podczas działania aplikacji. Dlatego kod wykonywalny nigdy nie jest 'kasowany' i mogę bezpiecznie przechowywać odniesienie do niego. Ale czy to prawda?

Author: Patrick, 2012-01-03

5 answers

Czy Mogę zapisać funkcję jako odniesienie, ponieważ std:: function jest tylko wskaźnikiem funkcji, a' kod wykonywalny ' funkcji jest gwarantowany, że pozostanie w pamięci?

std::function nie jest tylko wskaźnikiem funkcji. Jest to owijarka wokół dowolnie wywołanego obiektu i zarządza pamięcią używaną do przechowywania tego obiektu. Podobnie jak w przypadku każdego innego typu, bezpieczne jest przechowywanie referencji tylko wtedy, gdy masz inny sposób, aby zagwarantować, że dany obiekt jest nadal ważny ilekroć stosuje się to odniesienie.

Jeśli nie masz dobrego powodu do przechowywania referencji i sposobu, aby zagwarantować, że pozostanie ona ważna, przechowuj ją według wartości.

Przekazywanie przez const referencji do konstruktora jest bezpieczne i prawdopodobnie bardziej efektywne niż przekazywanie wartości. Przekazywanie referencji Nie - const jest złym pomysłem, ponieważ uniemożliwia przekazywanie tymczasowej, więc użytkownik nie może bezpośrednio przekazać lambda, wyniku bind, ani żadnego innego wywoływalnego obiektu poza samym std::function<int(int)>.

 48
Author: Mike Seymour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-03 11:19:38

Jeśli przekażesz funkcję konstruktorowi przez odniesienie i nie zrobisz jej kopii, będziesz miał pecha, gdy funkcja wyjdzie poza zakres tego obiektu, ponieważ odniesienie nie będzie już ważne. To już zostało powiedziane w poprzednich odpowiedziach.

Chciałem dodać, że zamiast tego można przekazać funkcję przez wartość , a nie referencję, do konstruktora. Dlaczego? Cóż, i tak potrzebujesz jego kopii, więc jeśli przekazujesz wartość kompilator może optymalizacja eliminuje potrzebę tworzenia kopii, gdy przekazywana jest tymczasowa (np. wyrażenie lambda napisane na miejscu).

Oczywiście, niezależnie od tego, co robisz, potencjalnie robisz kolejną kopię, gdy przypisujesz funkcję przekazaną do zmiennej, więc użyj std::move, aby wyeliminować tę kopię. Przykład:

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

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

private:
   MyFunction m_myFunction;
};

Tak więc, jeśli zostaną przekazane w wartości R do powyższego, kompilator optymalizuje pierwszą kopię do konstruktora, a std::move usuwa drugą:)

If your (only) konstruktor pobiera referencję const, ty będziesz musiał wykonać jej kopię w funkcji niezależnie od tego, jak zostanie przekazana.

Alternatywą jest zdefiniowanie dwóch konstruktorów, aby radzić sobie z wartościami lvalu i rvalue oddzielnie:

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

Teraz, ręcznie rvalues inaczej i eliminuje potrzebę kopiowania w tym przypadku przez jawną obsługę (zamiast pozwolić kompilator obsłużyć go za Ciebie). Może być nieznacznie bardziej wydajny niż pierwszy, ale jest również bardziej kodowany.

The (prawdopodobnie widziałem tu sporo) odpowiednie odniesienie (i bardzo dobra lektura): http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

 10
Author: jsdw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-19 11:59:14

Jako ogólna zasada (szczególnie jeśli używasz ich dla jakiegoś wysoce gwintowanego systemu), podaj wartość. Naprawdę nie ma sposobu, aby zweryfikować z poziomu wątku, że podstawowy obiekt nadal istnieje z typem odniesienia, więc otwierasz się na bardzo paskudną rasę i błędy impasu.

Kolejną kwestią są ukryte zmienne stanu w funkcji std::, dla których modyfikacja jest mało prawdopodobna, aby była bezpieczna dla wątku. Oznacza to, że nawet jeśli wywołanie funkcji bazowej jest thread-safe, wywołanie "()" STD::function wrapper może nie być. Możesz odzyskać pożądane zachowanie zawsze używając lokalnych kopii funkcji std::, ponieważ każda z nich będzie miała izolowaną kopię zmiennych stanu.

 3
Author: Zack Yezek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 18:24:34

Proponuję zrobić kopię:

MyFunction m_myFunction; //prefferd and safe!

Jest to bezpieczne, ponieważ jeśli oryginalny obiekt wyjdzie poza zakres, niszcząc się, Kopia nadal będzie istnieć w instancji klasy.

 2
Author: Nawaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-03 11:20:27

Kopiuj ile chcesz. Jest kopiowalny. Większość algorytmów w bibliotece standardowej wymaga, aby funktory były.

Jednak przekazywanie przez odniesienie będzie prawdopodobnie szybsze w nietrywialnych przypadkach, więc sugerowałbym przekazywanie przez stałe odniesienie i przechowywanie przez wartość, abyś nie musiał przejmować się zarządzaniem cyklem życia. Więc:

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

Przekazując referencję stałą lub rvalue, obiecujesz wywołującemu, że nie zmodyfikujesz funkcji, dopóki możesz ją wywołać. Zapobiega to zwykle należy unikać modyfikowania funkcji przez pomyłkę i wykonywania jej celowo, ponieważ jest ona mniej czytelna niż użycie zwracanej wartości.

Edit: pierwotnie powiedziałem" stała lub rvalue "powyżej, ale komentarz Dave' a zmusił mnie do tego i rzeczywiście rvalue reference nie akceptuje lvalues.

 2
Author: Jan Hudec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-23 09:45:49