2013-05-11 64 views
8

我有兩個半密切相關的問題。給定一個作爲模板參數傳遞的STL迭代器類型:檢查/修改迭代器「constness」

  1. 如何確定類型是否對應於常量或非常量迭代器?
  2. 作爲1的替代方法,如何施加(例如使用enable_if s)此類型對應於非常量迭代器?
  3. 如何從非const函數獲得迭代器的const版本(反之亦然)? [注:在this post中回答;不出所料,你不能。 ]

來自哪裏這樣的問題:

我寫了一個小班,以便於向量運算/關係/代數運算(按向量我的意思是一維固定大小的數據,而不是STL載體)。我沒有強加特定的數據容器,而是定義了一個接口並派生出幾個可能的容器,這些容器基本上是「包裝」各種存儲數據的方式。其中一個容器是STL迭代器的包裝器,我遇到了一些麻煩。

+0

你可以從['std :: is_const '](http://en.cppreference.com/w/cpp/types/is_const)開始。 – BoBTFish 2013-05-11 15:26:32

+1

@BoBTFish我試過了,但不起作用。 'const_iterator'和'const iterator'是兩個不同的東西。試試這個:http://pastebin.com/kKQEQthj – Sheljohn 2013-05-11 15:33:55

+0

@BoBTFish對不起,你是否編輯了你的評論?我可以發誓我沒有看到'decltype(* it)'當我發佈我的最後一條評論時..你也會對我也是這樣:) – Sheljohn 2013-05-11 15:49:46

回答

6

問題1:

你可以使用下列類型的特點:

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 { }; 

這裏是一個演示:

#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 
} 

這裏是一個live example

問題2:

利用上述類型的性狀,這變得簡單。假設你有要約束,使其只接受非const迭代函數模板foo()

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

和一個簡單的演示程序:

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

這裏是一個live example

+0

我只是想過使用type :: traits中的':: reference' typedef ..但你的答案是無可挑剔的!謝謝:) – Sheljohn 2013-05-11 15:37:30

+0

@ Sh3ljohn:很高興幫助:) – 2013-05-11 15:38:51

+0

只是一個問題:爲什麼不在你的例子中使用'typename = typename std :: enable_if <!is_const_iterator :: value> :: type'作爲「模板過濾器」問題2?這是使用我最常見到的enable_ifs的方式,但如果出現問題,我想知道:) – Sheljohn 2013-05-11 17:42:20

3

爲1),你可以做這樣的事情:

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

或者這樣:

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

您可以使用這些謂詞傳遞給std::enable_if實施2)。

注:正如評論所指出的R.費爾南德斯Martinho,如果有問題的迭代器使用不同類型的比其reference性狀(如std::vector<bool>::const_iterator做)的普通引用這些謂詞將失敗。

+0

非常感謝!我想你是第一個發佈答案的人,而@AndyProwl的答案與你的答案相同,所以我不知道要標記哪一個。只是一個問題:爲什麼使用'decltype'? – Sheljohn 2013-05-11 15:40:24

+1

@ Sh3ljohn'* iterator'是一個*表達式,*但是'std :: remove_reference'需要一個* type *(因爲它是一個帶模板類型參數的模板)。因此,您必須以某種方式獲取表達式的類型 - 這就是decltype的用途。 – Angew 2013-05-11 15:51:23

+0

如果'decltype(* iterator)'是一個代理而不是引用? – 2013-05-11 15:57:01

2

你可以在

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

或(XEO的偏好)

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

,檢查是否提領該迭代給你的東西分配使用SFINAE。不完美,如果集合的元素類型不可分配,但不管怎麼樣都有什麼好處?

不要測試const_iterator,測試算法實際需要的操作。

+2

對於一切的愛,*使用'* std :: declval ()'*。 – Xeo 2013-05-11 15:44:37

+0

@Xeo:爲什麼?這大約有20個額外的字符是爲了什麼好處? – 2013-05-11 15:46:23

+0

可讀性...? – 0x499602D2 2013-05-11 15:47:24