2012-02-23 14 views
7

Implementacja std::vector dostarczana z Visual Studio 2010 i wcześniejszymi wersjami ma dobrze znaną specyfikację: metoda resize ma następujący podpis (zgodny z C++ 03) :Samodzielna, zgodna z STL implementacja std :: vector

void resize(size_type new_size, value_type value); 

zamiast podpisu C++ 11 zgodnym, który jest używany przez większość innych implementacjach STL (jak STL gcc lub STLport) długości, po czym C++ 11:

void resize(size_type new_size, const value_type& value); 

The Problem z pierwszym wariantem polega na tym, że w niektórych sytuacjach ia, to nie skompilować jeśli value_type posiada specyfikację wyrównania:

struct __declspec(align(64)) S { ... }; 
std::vector<S> v; // error C2719: '_Val': formal parameter with __declspec(align('64')) won't be aligned 

To wellknown problemem bez zadowalającego obejście oprócz używając innej realizacji std::vector.

szukam dobrze napisana, dobrze przetestowany, samowystarczalnego i STL kompatybilne realizacji std::vector z licencją MIT stylu, że mogę wpaść do mojego projektu jako pojemnik z wyboru Wyrównany typy.

Rozważałem wyodrębnienie go ze STLport lub STL-a gcc, ale będąc w pełni zgodnym z normą, oba są duże i zawierają wiele nietrywialnych zależności.

(byłbym całkowicie zadowolony z realizacji rozsądnym podzbioru std::vector które obsługują tylko push_back, clear, capacity, size, reserve, resize, swap i indeksowania tablicy.)

Jakieś pomysły?

+0

Nie widzę, jak metoda '.resize()' powoduje, że deklaracja 'std :: vector v;' kończy się niepowodzeniem. Tworzenie instancji szablonu klasy nie tworzy instancji swoich metod, tylko tych używanych. (Tj. Domyślny ctor i dtor w tym przypadku). – MSalters

+0

Domyślam się, że błąd jest emitowany podczas parsowania ... Należy pamiętać, że mówimy tutaj o naprawdę specyficznym dla kompilatora problemie. –

Odpowiedz

8

Faceci za biblioteką Eigen wydają się znaleźć ładne obejście problemu przechowywania „nadmiernie wyrównane rodzajów” (as Stephan T. Lavavej call them) do std::vector wprowadzone w Visual Studio STL.

Ich realizacja wydaje się niepotrzebna skomplikowana (sprawdź źródła here i here), ale główną ideą jest, aby upakować typ, który jedzie do std::vector cienką otoki:

#include <vector> 

template <typename T> 
struct wrapper : public T 
{ 
    wrapper() {} 
    wrapper(const T& rhs) : T(rhs) {} 
}; 

struct __declspec(align(64)) S 
{ 
    float x, y, z, w; 
}; 

int main() 
{ 
    std::vector< wrapper<S> > v; // OK, no C2719 error 
    return 0; 
} 

O realizacji w Eigen, ja należy przyznać, że nie bardzo rozumiem

  • dlaczego muszą Eigen::aligned_allocator_indirection,
  • dlaczego muszą dokonać n wyjątek dla typów arytmetycznych w EIGEN_WORKAROUND_MSVC_STL_SUPPORT,
  • dlaczego muszą określić wszystkie te konstruktorów i operatorów w Eigen::workaround_msvc_stl_support,
  • lub dlaczego muszą przedefiniować resize w ich częściowej specjalizacji std::vector dla Eigen::aligned_allocator_indirection podzielnika ...

Clues welcome. Chodzi o to, że ta sztuczka działa perfekcyjnie (o ile mogę to powiedzieć) i nie widzę w tym nic złego, może z niewielkiej nieelegancji.

1

Najprostszą (i najlepszą opcją, imho) byłoby zapewnienie bezpłatnej funkcji jako rozszerzenia interfejsu vector, co jest właściwe. Funkcje trzeba zaimplementować resize są dostępne z poziomu interfejsu publicznego std::vector:

#include <vector> 

template<class T, class Alloc> 
void resize(std::vector<T, Alloc>& v, 
    typename std::vector<T, Alloc>::size_type new_size, T const& val) 
{ 
    if (v.size() < new_size) 
     v.insert(v.end(), new_size - v.size(), val); 
    else if (new_size < v.size()) 
     v.erase(v.begin() + new_size, v.end()); 
} 

A dla spójności również wersji dla jednego argumentu:

template<class T, class Alloc> 
void resize(std::vector<T, Alloc>& v, 
    typename std::vector<T, Alloc>::size_type new_size) 
{ 
    v.resize(new_size); // simply forward 
} 

Jeśli jednak po prostu chcesz rozwijanym w nowym wektorem i nie martwić się o wolne lub członkiem funkcji, innym rozwiązaniem jest po prostu podklasy std::vector:

#include <vector> 
#include <memory> 

template<class T, class Alloc = std::allocator<T>> 
class myvector 
    : public std::vector<T, Alloc> 
{ 
    typedef std::vector<T, Alloc> base; 
public: 
    typedef typename base::size_type size_type; 

    void resize(size_type new_size){ 
    base::resize(new_size); 
    } 

    void resize(size_type new_size, T const& val){ 
    if (this->size() < new_size) 
     this->insert(this->end(), new_size - this->size(), val); 
    else if (new_size < this->size()) 
     this->erase(this->begin() + new_size, this->end()); 
    } 
}; 

Należy zauważyć, że udostępniłem również wersję z pojedynczym argumentem resize, ponieważ dwie wersje argumentów ukrywałyby wszystkie wersje klasy podstawowej. Zauważ także, że muszę poprzedzić wszystkie wywołania funkcji składowych za pomocą this->, ponieważ są one zależne od klasy bazowej std::vector i jako takie na argumentach szablonu.

+0

+1, choć chciałbym zauważyć, że podklasowanie jest na granicy ... –

+1

Podklasowanie 'std :: vector' było jednym z moich pierwszych podejść, niestety nadal nie można go skompilować, ponieważ na końcu' std :: vector' nadal tworzy instancję . –

+1

@ FrançoisBeaune: Ach, to dość niefortunne. :/Spróbuj skomentować wersję 'std :: vector' lub po prostu popraw podpis. : P FWIW, VS11 nadal nie rozwiązuje tego problemu. – Xeo