2013-05-11 24 views
8

Mam dwa i pół ściśle ze sobą powiązane pytania. Podano parametr iteratora STL przekazany jako parametr szablonu:Sprawdź/zmodyfikuj iterator "constness"

  1. Jak ustalić, czy typ odpowiada stałowodziejowi lub stałemu iteratorowi?
  2. Alternatywnie do 1., jak nałożyć (używając na przykład enable_if s), że ten typ odpowiada niestanowiącemu stałej iteracji?
  3. Jak uzyskać constendę iteratora z nie stałej (i na odwrót:)? [Uwaga: odebrano w this post; nic dziwnego, nie możesz. ]

Skąd to pytanie pochodzi z:

napisałem małą klasę, aby ułatwić arytmetycznych/relacyjnych/operacje algebraiczne na wektorach (przez wektor Znaczy 1d danych o stałym rozmiarze, a nie Wektory STL). Zamiast narzucać określony kontener danych, zdefiniowałem interfejs i wyprowadziłem kilka możliwych kontenerów, które zasadniczo "zawijają" różne sposoby przechowywania danych. Jednym z tych kontenerów jest wrapper dla Iteratorów STL i mam z tym pewne problemy.

+0

Można zacząć od ['std :: is_const '] (http://en.cppreference.com/w/cpp/types/is_const). – BoBTFish

+1

@BoBTFish Próbowałem, ale nie działa. 'const_iterator' i' const iterator' to dwie różne rzeczy. Spróbuj tego: http://pastebin.com/kKQEQthj – Sheljohn

+0

@BoBTFish Przepraszamy, edytowałeś swój komentarz? Mógłbym przysiąc, że nie widziałem "decltype (* it)", kiedy opublikowałem moją ostatnią uwagę. Z pewnością byłbyś z tym dobry :) – Sheljohn

Odpowiedz

6

Pytanie 1:

Można stosować następujące rodzaje cechę:

template<typename T, typename = void> 
struct is_const_iterator : std::false_type { }; 

template<typename T> 
struct is_const_iterator<T, 
    typename std::enable_if< 
     std::is_const< 
      typename std::remove_pointer< 
       typename std::iterator_traits<T>::pointer 
       >::type 
      >::value 
     >::type> : std::true_type { }; 

Oto demonstracji:

#include <type_traits> 
#include <iterator> 
#include <list> 
#include <vector> 

template<typename T, typename = void> 
struct is_const_iterator : std::false_type { }; 

template<typename T> 
struct is_const_iterator<T, 
    typename std::enable_if< 
     std::is_const< 
      typename std::remove_pointer< 
       typename std::iterator_traits<T>::pointer 
       >::type 
      >::value 
     >::type> : std::true_type { }; 

int main() 
{ 
    typedef std::list<int>::iterator LI; 
    typedef std::list<int>::const_iterator CLI; 
    static_assert(is_const_iterator<LI>::value, "!"); // Fires 
    static_assert(is_const_iterator<CLI>::value, "!"); // Does not fire 

    typedef std::vector<int>::iterator VI; 
    typedef std::vector<int>::const_iterator CVI; 
    static_assert(is_const_iterator<VI>::value, "!"); // Fires 
    static_assert(is_const_iterator<CVI>::value, "!"); // Does not fire 
} 

A oto live example.

Pytanie 2:

Z powyższego typu cecha ta staje się prosta. Załóżmy, że masz szablon funkcji foo() które chcesz ograniczyć tak, że przyjmuje tylko innych niż const iteratorów:

template<typename It, 
    typename std::enable_if<!is_const_iterator<It>::value>::type* = nullptr> 
void foo(It i) 
{ 
    // Does something with i... 
} 

oraz prosty program demonstracyjny:

int main() 
{ 
    std::vector<int> v; 
    foo(v.begin()); // OK 
    foo(v.cbegin()); // ERROR! 
} 

A oto live example.

+0

Po prostu pomyślałem o użyciu ':: reference' typedef również z cech typu .. Ale twoja odpowiedź jest nienaganna! Dzięki :) – Sheljohn

+0

@ Sh3ljohn: Cieszę się, że pomogło :) –

+0

Po prostu pytanie: dlaczego nie używać "typename = nazwa_pliku std :: enable_if :: wartość> :: typ" jako "szablon-filtr" w twoim przykładzie na pytanie 2? W ten sposób korzystam z włączonych_programów, które widziałem najczęściej, ale jeśli coś jest nie tak, chciałbym wiedzieć co :) – Sheljohn

3

za 1), można zrobić coś takiego:

std::is_const< 
    typename std::remove_reference< 
    typename std::iterator_traits<Iterator>::reference 
    >::type 
>::value 

albo to:

std::is_const< 
    typename std::remove_reference< 
    decltype(*iterator) 
    >::type 
>::value 

Można użyć tych predykatów przejść do std::enable_if wdrożyć 2).

UWAGA: Jak podkreśla R. Martinho Fernandes w komentarzach, te predykaty nie powiedzie się, jeśli w pytaniu iterator używa innego typu niż zwykły odniesień do jego reference cechy (takie jak std::vector<bool>::const_iterator robi).

+0

Wielkie dzięki! Chyba najpierw napisałeś odpowiedź, a @AndyProwl ma tę samą odpowiedź od ciebie, więc nie wiem, który z nich zaznaczyć. Tylko pytanie: po co używać 'decltype'? – Sheljohn

+1

@ Sh3ljohn '* iterator' to wyrażenie *, ale' std :: remove_reference' wymaga typu * * (ponieważ jest to szablon z parametrem typu szablonu). Dlatego musisz jakoś uzyskać typ wyrażenia - i właśnie do tego służy 'decltype'. – Angew

+0

Co jeśli 'decltype (* iterator)' jest proxy, a nie referencją? –

2

Można użyć SFINAE na

decltype(**(T*)0 = std::move(**(T*)0)) 

(lub preferencji użytkownika Xeo)

decltype(*declval<T&>() = std::move(*declval<T&>())) 

który sprawdza czy dereferencing iteracyjnej daje coś, regulowany. Nie jest doskonały, jeśli typ elementu kolekcji nie jest możliwy do przypisania, ale czy w takim razie dobrze byłoby mieć nie-const_iterator?

Nie testuj pod kątem const_iterator, przetestuj działanie, które faktycznie potrzebuje twój algorytm.

+2

Z miłości do wszystkiego * używaj '* std :: declval ()' *. – Xeo

+0

@Xeo: Dlaczego? To około 20 dodatkowych znaków, za jakie korzyści? –

+0

Czytelność ...? – 0x499602D2