Algorytmy STL na tablicach

tech • 292 słowa • 2 minuty czytania

Często w programach napisanych w C do pobierania rozmiaru tablic (ilości elementów) alokowanych przez kompilator na stosie stosuje się prostą konstrukcję z operatorem sizeof. ładnie “opakowaną” w makro do łatwego użycia:

#define SizeOfArray(array)	sizeof(array) / sizeof(array[0])

W C++ zamiast makra lepiej wykorzystać wzorzec, a wtedy można to przedstawić w takiej postaci:

template<typename T, size_t N>
inline size_t SizeOfArray(const T (&)[N]) {
	return N;
}

Wersja cpplusowa jest bezpieczniejsza. Próba wywołania SizeOfArray() na wskaźniku zakończy się błędem w czasie kompilacji, w przeciwieństwie do makra, które dopiero da znać o tym problemie w run-time.

To tyle słowami wstępu, bo nie chciałem zbytnio o tym teraz pisać.

Głównym tematem miała być wzmianka o prostej możliwości używania algorytmów pochodzącym z biblioteki STL na zwykłych tablicach. Większość algorytmów działa poprawnie na wskaźnikach, tak jak gdyby były iteratorami.

int array[] = { 1, 2, 3, 4, 5 };
std::copy(array, array + SizeOfArray(array), std::ostream_iterator<int>(std::cout, " "));

Dla nieco lepszego i czytelniejszego kodu, można dopisać 2 szablonowe funkcje służące do pobierania “iteratorów” dla “surowej” tablicy, załatwiając tym konieczność pamiętania o dodawaniu rozmiaru przy wskaźniku “na koniec”.

template<typename T, size_t N>
inline T* begin(T (&array)[N]) {
	return array;
}

template<typename T, size_t N>
inline T* end(T (&array)[N]) {
	return array + N;
}

Teraz kod wykorzystujący algorytm std::copy na tablicy wygląda znacznie lepiej:

int array[] = { 1, 2, 3, 4, 5 };
std::copy(begin(array), end(array), std::ostream_iterator<int>(std::cout, " "));

Idąc dalej można byłoby specjalizować powyższe funkcje również dla zwykłych iteratorów. Wtedy będzie można używać funkcji begin() i end() w jednakowy sposób dla zwykłych tablic i kontenerów z biblioteki STL.

Na jakimś forum przypadkiem trafiłem właśnie na takie specjalizacje, ponoć w starej (?) implementacji array w boost było to używane, ale nie mogę tego potwierdzić. Napisanie tego zostawiam czytelnikom jako zadanie domowe ;)

Komentarze (4)

yarpen avatar
yarpen
20090402-114007-yarpen

Problem z wersja SizeOfArray jest taki, ze nie resolve’uje sie ona w czasie kompilacji. Wersje bez tej wady przedstawilem swego czasu tutaj: http://msinilo.pl/blog/?p=64
(BTW, nazwa tez nie najszczesliwsza, bo SizeOfArray to po prostu sizeof(array)).

Malcom avatar
Malcom
20090402-120133-malcom

Dziwne, bo przecież operator sizeof jak i wzorce są rozwijane w czasie kompilacji. Zrobiłem nawet testy i dla tablicy z 5 intami, VC9 i GCC3.4.x dla obu wersji wygenerował podobny kod z liczbą 5, bez żadnych dodatkowych operacji jakie musiałyby być wykonane w czasie działania.

No tak, “Count” w nazwie byłby tutaj lepszy.

Odwiedzam co jakiś czas Twoją stronę, tam chyba kiedyś widziałem to rozwiązanie ;)

yarpen avatar
yarpen
20090402-154415-yarpen

Operacji dodatkowych nie ma, ale takie cos nie przejdzie:

int array2[SizeOfArray(array)];

Po prostu kompilator nie jest w stanie okreslic rezultatu (teoretycznie moglby, ale w C++ tego nie zrobi).

Malcom avatar
Malcom
20090402-161750-malcom

Szkoda, jak dobrze pamiętam, C++0x coś zmienia w tej kwestii przez wprowadzenie constexpr.

Dodaj komentarz

/dozwolony markdown/

/nie zostanie opublikowany/