2013-05-23 6 views
15

std::vector jest niestabilnym pojemnikiem, tj. Przez zmianę rozmiaru wektora, iteratory mogą zostać unieważnione. W przeciwieństwie do tego, std::list lub boost::container::stable_vector są stabilnymi pojemnikami, które zachowują ważność iteratorów do momentu usunięcia odpowiedniego elementu.Jak sprawdzić, czy pojemnik jest stabilny?

Czy istnieje sposób sprawdzenia, czy dany kontener jest stabilny? Na przykład, jeśli mam coś takiego jak

Czy można zezwolić tylko na stabilne kontenery i zakazać niestabilnych?

+1

Interesujące pytanie. –

+0

Dlatego potrzebujemy koncepcji! (Myślę) –

+0

@MarkGarcia: Właśnie dlatego potrzebujemy * aksjomatów *. Pojęcia bez aksjomatów nie mogą uchwycić takich semantycznych wymagań. Nie jestem pewien, czy niedługo je otrzymamy, choć ... –

Odpowiedz

6

Nie sądzę, że istnieje jakakolwiek dostępna informacja, ale można napisać własną cechę. Musisz jednak specjalizować go dla każdego stabilnego kontenera, który może być użyty, co prawdopodobnie nie jest możliwe.

#include <boost/container/vector.hpp> 

#include <iostream> 
#include <type_traits> 
#include <list> 
#include <vector> 

template <template <typename...> class Container> 
struct is_stable 
    : std::false_type 
{}; 

template <> 
struct is_stable<std::list> 
    : std::true_type 
{}; 

template <> 
struct is_stable<boost::container::stable_vector> 
    : std::true_type 
{}; 

template<template <typename...> class Container = std::list> 
class Foo 
{ 
    static_assert(is_stable<Container>::value, "Container must be stable"); 
}; 

int main() 
{ 
    Foo<std::list> f1; // ok 
    Foo<std::vector> f2; // compiler error 
} 

Nie sądzę, że istnieje sposób, w jaki można automatycznie wykryć, że kontener jest stabilny, bez uciekania się do ręcznej specjalizacji.


Tak dla zabawy, próbowałem pisać co pojęcie/aksjomat dla stabilności będzie wyglądać (concepts i axioms są rozszerzeniem języka były considered for inclusion in C++11):

concept StableGroup<typename C, typename Op> 
    : Container<C> 
{ 
    void operator()(Op, C, C::value_type); 

    axiom Stability(C c, Op op, C::size_type index, C::value_type val) 
    { 
     if (index <= c.size()) 
     { 
      auto it = std::advance(c.begin(), index); 
      op(c, val); 
      return it; 
     } 
     <-> 
     if (index <= c.size()) 
     { 
      op(c, val); 
      return std::advance(c.begin(), index); 
     } 
    } 
} 

Jeśli to poprawnie przechwytuje wymóg, że każdy iterator nad oryginalnym kontenerem jest równoważny z odpowiednim iteratorem nad zmodyfikowanym kontenerem. Nie jestem pewien, czy to bardzo przydatne, ale wymyślenie takich aksjomatów jest ciekawym ćwiczeniem :)!

+1

To jest absolutnie poprawne - stabilne/niestabilne jest bardzo abstrakcyjnym pomysłem. Wektor może łatwo nie unieważnić swoich iteratorów: nie może zmienić rozmiaru tablicy, kiedy myślisz, że to robi, lub nawet jeśli to robi, może być w stanie rozszerzyć istniejący blok pamięci, którego używa (w zależności od kompilatora itp.). Jeśli napiszę własny pojemnik, może on łatwo nie deterministycznie ponownie przydzielić pamięć. – cristicbz

+0

Ta cecha ulega uszkodzeniu, jeśli nie dodasz nagłówka z 'boost :: stable_vector', na przykład. Aby być ogólnie użytecznym, musisz również przesłać dalej deklarację wszystkich klas, dla których specjalizujesz się. – jrok

+0

@jrok: Tak, dlatego to rozwiązanie jest dalekie od ideału. Idealnie byłoby, gdyby biblioteki kontenerowe były odpowiedzialne za specjalizację tej cechy, ale oczywiście nie jest to możliwe. Inną możliwością jest pozostawienie cechy niespecjalizowanej (poza najpopularniejszymi kontenerami) i wskazanie użytkownikom "Foo", że jeśli chcą użyć innego pojemnika, muszą specjalizować tę cechę: 'static_assert' zwróci ich uwagę na tę część dokumentacja, która powinna wyraźnie zaznaczyć, że pojemnik musi być stabilny. Nadal nie jest to bardzo wygodne. –