Czy można bezpiecznie odepchnąć element z tego samego wektora?

vector<int> v;
v.push_back(1);
v.push_back(v[0]);

Jeśli drugi push_back spowoduje realokację, odniesienie do pierwszej liczby całkowitej w wektorze nie będzie już ważne. Więc to nie jest bezpieczne?

vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
To sprawia, że jest bezpiecznie?
Author: TemplateRex, 2013-09-13

9 answers

Wygląda jak http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 rozwiązał ten problem (lub coś bardzo podobnego do niego) jako potencjalną wadę w standardzie:

1) Parametry pobrane przez const reference mogą być zmieniane podczas wykonywania funkcji

Przykłady:

Given std:: vector v:

V. insert (V. begin (), v[2]);

V [2] można zmienić za pomocą ruchomych elementów wektora

Proponowane rozdzielczość była taka, że to nie wada:

Vector:: insert (ITER, value) jest wymagany do działania, ponieważ standard nie daje pozwolenia, żeby to nie działało.

 31
Author: Nate Kohl,
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-09-13 20:23:36

Tak, jest to bezpieczne, a standardowe implementacje bibliotek przeskakują przez obręcze, aby to zrobić.

Wydaje mi się, że implementatorzy śledzą ten wymóg w jakiś sposób do 23,2/11, Ale Nie wiem jak i nie mogę znaleźć czegoś bardziej konkretnego. Najlepsze co mogę znaleźć to ten artykuł:

Http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771

Inspekcja implementacji libc++i libstdc++pokazuje, że są one również bezpieczne.

 21
Author: Sebastian Redl,
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-09-13 14:58:42

Norma gwarantuje bezpieczeństwo nawet pierwszego przykładu. C++11

[Sekwencja.reqmts]

3 w tabelach 100 i 101 ... X oznacza klasę kontenera sekwencyjnego, a oznacza wartość X zawierającą elementy typu T,... t oznacza wartość lvalue lub const rvalue of X::value_type

16 tabela 101 ...

Wyrażenie a.push_back(t) Typ zwrotu void semantyka operacyjna dodaje kopię t. wymaga: T należy CopyInsertable do X. Pojemnik basic_string, deque, list, vector

Więc nawet jeśli nie jest to do końca trywialne, implementacja musi zagwarantować, że nie unieważni referencji podczas wykonywania push_back.

 13
Author: Angew,
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-09-13 15:04:38

Nie jest oczywiste, że pierwszy przykład jest bezpieczny, ponieważ najprostszą implementacją push_back byłoby najpierw ponowne przydzielenie wektora, w razie potrzeby, a następnie skopiowanie odniesienia.

Ale przynajmniej wydaje się być bezpiecznie z Visual Studio 2010. Jego implementacja push_back robi specjalną obsługę przypadku, gdy odepchniesz element w wektorze. Kod ma następującą strukturę:

void push_back(const _Ty& _Val)
    {   // insert element at end
    if (_Inside(_STD addressof(_Val)))
        {   // push back an element
                    ...
        }
    else
        {   // push back a non-element
                    ...
        }
    }
 7
Author: user763305,
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-09-15 13:46:38

To nie jest gwarancja ze standardu, ale jako kolejny punkt danych, {[1] } jest bezpieczny dla libc++ LLVM.

Libc++'s std::vector::push_back wywołania __push_back_slow_path, gdy trzeba ponownie przydzielić pamięć:

void __push_back_slow_path(_Up& __x) {
  allocator_type& __a = this->__alloc();
  __split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), 
                                                  size(), 
                                                  __a);
  // Note that we construct a copy of __x before deallocating
  // the existing storage or moving existing elements.
  __alloc_traits::construct(__a, 
                            _VSTD::__to_raw_pointer(__v.__end_), 
                            _VSTD::forward<_Up>(__x));
  __v.__end_++;
  // Moving existing elements happens here:
  __swap_out_circular_buffer(__v);
  // When __v goes out of scope, __x will be invalid.
}
 3
Author: Nate Kohl,
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-09-13 19:08:15

Pierwsza wersja zdecydowanie nie jest Bezpieczna:

Operacje na iteratorach uzyskiwane przez wywołanie kontenera biblioteki standardowej lub funkcji string member mogą uzyskać dostęp do kontenera bazowego, ale nie mogą go modyfikować. [Notatka: w szczególności, operacje kontenera, które unieważniają Iteratory, są sprzeczne z operacjami na iteratorach powiązanych z tym kontenerem. - uwaga końcowa]

Z sekcji 17.6.5.9


Zauważ, że jest to sekcja o wyścigach danych, które ludzie zwykle myślą w połączeniu z threading... ale rzeczywista definicja obejmuje relacje "happens before" I nie widzę tu żadnej Porządkowej zależności między wieloma efektami ubocznymi push_back, mianowicie unieważnienie odniesienia wydaje się nie być zdefiniowane jako uporządkowane w odniesieniu do kopiowania-konstruowania nowego elementu ogonowego.

 1
Author: Ben Voigt,
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-09-13 16:07:41

Jest całkowicie bezpieczny.

W drugim przykładzie masz

v.reserve(v.size() + 1);

, która nie jest potrzebna, ponieważ jeśli wektor wyjdzie poza swój rozmiar, to implikuje reserve.

Vector jest za to odpowiedzialny, nie ty.
 0
Author: Zaffy,
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-09-20 08:16:02

Oba są bezpieczne, ponieważ push_back skopiuje wartość, a nie referencję. Jeśli przechowujesz wskaźniki, nadal jest to bezpieczne, jeśli chodzi o wektor, ale pamiętaj, że będziesz mieć dwa elementy wektora wskazujące na te same dane.

Sekcja 23.2.1 Ogólne Wymagania Dotyczące Kontenerów

16
  • a. push_back (t) dodaje kopię t. wymaga: T powinna być CopyInsertable do X.
  • a. push_back (rv) dodaje kopię rv. Wymaga: T MoveInsertable do X.

Implementacje push_back muszą zatem zapewnić, że Kopia v[0] jest wstawiony. W przeciwnym przykładzie, zakładając, że implementacja zmieniłaby przydział przed skopiowaniem, nie dodałaby kopii v[0] i jako taka naruszałaby specyfikacje.

 -1
Author: OlivierD,
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-09-13 17:43:34

Od 23.3.6.5/1: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.

Ponieważ wstawiamy na końcu, żadne odwołania nie zostaną unieważnione Jeśli wektor nie zostanie zmieniony. Więc jeśli wektor capacity() > size() to jest gwarantowane działanie, w przeciwnym razie jest zagwarantowane nieokreślone zachowanie.

 -2
Author: Mark B,
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-09-13 14:59:43