2010-02-03 8 views
7

Tworzę konstruktor, który zajmie parę iteratorów wejściowych. Chcę podpis sposób mieć kompilacji const semantyka podobny do:Jak wymagać semantyki const_iterator w sygnaturze funkcji szablonu?

DataObject::DataObject(const char *begin, const char *end) 

Jednak nie mogę znaleźć żadnych przykładów. Na przykład, konstruktor zakres mojego STL implementacja dla vector jest zdefiniowany jako:

template<class InputIterator> 
vector::vector(InputIterator first, InputIterator last) 
{ 
    construct(first, last, iterator_category(first)); 
} 

który nie ma czasu kompilacji const gwarancje. iterator_category/iterator_traits<> również nie zawierają nic związanego z const.

Czy istnieje sposób wskazania, aby zagwarantować dzwoniącemu, że nie mogę zmodyfikować danych wejściowych?

edycja, 2010-02-03 16:35 UTC

Jako przykład jak chciałbym korzystać z funkcji, chciałbym być w stanie przejść parę char* wskaźników i wiem, na podstawie podpisu funkcji, że dane, na które wskazują, nie zostaną zmodyfikowane.
Miałem nadzieję, że uda mi się uniknąć utworzenia pary wskaźników const char*, aby zagwarantować semantykę const_iterator. W tym przypadku mogę zostać zmuszony do zapłaty podatku od szablonów.

+0

Czy tego rodzaju rzeczy byłyby dobre dla koncepcji kompilatorowych? Nie pamiętam, czy propozycja mówi cokolwiek o wymaganiach stałych. – mskfisher

+0

Myślę, że najlepszą możliwą opcją w tym momencie jest jawne utworzenie instancji za pomocą 'const char *' i poleganie na tym jako sprawdzanie w czasie kompilacji dla wszystkich innych typów. – mskfisher

Odpowiedz

2

można po prostu stworzyć atrapę funkcję, która wywołuje szablon z char * const wskaźników. Jeśli twój szablon próbuje zmodyfikować swoje cele, twoja fikcyjna funkcja nie będzie się kompilować. Następnie możesz umieścić manekina wewnątrz osłon #ifndef NDEBUG, aby wykluczyć go z wersji wydań.

+0

To bardziej przypomina to. Daje mi to pewność kompilacji, której szukałem. – mskfisher

+0

... Chociaż dla moich celów, myślę, że masz na myśli wskaźniki "const char *". – mskfisher

+0

Tak - Doh! Rachunek! –

2

Co

#include <vector> 

template <class T> 
class MyClass{ 
public: 
    MyClass(typename T::const_iterator t1,typename T::const_iterator t2){ 
    } 
    // *EDITED*: overload for pointers (see comments) 
    MyClass(const T* t1,const T* t2){ 
    } 
}; 

void main(){ 
    std::vector<int> v; 
    std::vector<int>::const_iterator it1 = v.begin(); 
    std::vector<int>::const_iterator it2 = v.end(); 
    MyClass<std::vector<int> > mv(it1,it2); 

    // with pointers: 
    char* c1; 
    char* c2; 
    MyClass mc(c1,c2); 
} 
+0

Zapobiegasz odliczaniu parametrów szablonu za pomocą typów wewnętrznych. –

+0

Dobry pomysł, ale nie mam tutaj const_iterator, ponieważ przekazuję dwa "char *" jako iteratory. – mskfisher

+0

Dodaj przeciążenie dla pary wskaźników. – UncleBens

0

To konstruktor wektor odbiera swoich argumentów przez wartość, co oznacza, że ​​iteratory wskazówki dzwoniącego są kopiowane przed użyciem w konstruktorze, co oczywiście oznacza, że ​​nic nie dzieje się iteratorów rozmówcy.

const dla argumentów wejściowych ma znaczenie, gdy przechodzisz przez odniesienie. na przykład

void foo(int& x)

vs

void foo(const int& x)

w pierwszym przykładzie, wejście rozmówcy na x można modyfikować foo. W drugim przykładzie może nie być, tak jak odniesienie, const.

+0

Niestety, nie było całkowicie jasne w moim pytaniu. Chcę się upewnić, że dane, na które wskazują iteratory, są traktowane jako const, a nie że same iteratory powinny być stałe. – mskfisher

+1

Nie można użyć const_iterator do modyfikowania elementu kontenera, który iteruje. Zwykły iterator * może * być używany w ten sposób, nawet jeśli jest przekazywany przez odniesienie do stałej. (Po prostu utwórz jego nie stałą kopię.) Typy 'const iterator' i' const_iterator' to nie to samo. –

9

Osoba dzwoniąca może po prostu użyć szablonu z stałymi iteratorami. Jeśli tak, a kompilator nie uskarża się, jest zagwarantowane, że funkcja nie modyfikuje danych. Gdyby zmodyfikować dane, utworzenie szablonu za pomocą constewater doprowadziłoby do błędów.

Nie musisz naprawdę wymusić wywołującego, aby używać iteratorów const tylko dlatego, że niczego nie modyfikujesz.

+0

+1 ... i muszę powiedzieć, że mam problem ze wskazaniem, jak to zrobić! –

+0

Semantyka przykładu 'DataObject' oznacza, że ​​mogę przekazywać wskaźniki inne niż' constst' i traktować je jako wskaźniki "const". Nie chcę * zmuszać * osoby wywołującej do korzystania z iteratorów 'const' - po prostu chcę zagwarantować, że będą one * używane * tylko jako" const "iteratory. – mskfisher

+0

Tworzysz defensywne kopie danych, aby się nie zmieniały (wewnętrzny znak konstelacji *), lub dokumentujesz swoje wymagania i żyjesz z życiem. – KitsuneYMG

0

To proste (ale nie całkiem), czy można sobie pozwolić doładowania:

#include <boost/static_assert.hpp> 
#include <boost/type_traits.hpp> 

template<class It> 
void f(It b, It e) 
{ 
    using namespace boost; 
    typedef typename std::iterator_traits<It>::reference reference; 
    BOOST_STATIC_ASSERT(is_const<typename remove_reference<reference>::type>::value); 
} 

void test() 
{ 
    f((char const*)0, (char const*)0); // Compiles 
    f((char*)0, (char*)0); // Does not compile 
} 

EDIT: jeśli chcesz mieć i wskazanie na ten temat w podpisie to często można wykorzystać nazwę szablon parametr:

template<class ConstIt> 
void f(ConstIt b, ConstIt e) 
... 
+1

Jak rozumiem, kwestią pytania jest tylko upewnienie się, że sama funkcja nie może zmodyfikować sekwencji. Nie chodzi o zmuszanie użytkownika do ręcznego rzucania rzeczy do stałych iteratorów. – UncleBens

+0

Tak jak powiedziałem w swoim komentarzu do @sth, chcę wymagać, aby iterator mógł być * użyty * jako const_iterator, podobnie jak iterator z dostępem losowym może być użyty jako iterator do przodu. – mskfisher