2014-07-10 58 views
3

我正在將大量代碼從gcc移植到Visual Studio 2013.以下代碼示例在gcc 4.4上正常工作(!),但在VS2013上編譯begin()end()失敗:STL const_iterator強制轉換 - 編譯器差異

錯誤C2440: ':無符號字符*' 不能從轉換'到 '的std :: _ Vector_const_iterator >>'

class foo { 
    unsigned char* value; 
    int length; 

    std::vector<unsigned char>::const_iterator begin(); 
    std::vector<unsigned char>::const_iterator end(); 
}; 

std::vector<unsigned char>::const_iterator foo::begin() { 
    return std::vector<unsigned char>::const_iterator(value); 
} 

std::vector<unsigned char>::const_iterator foo::end() { 
    return std::vector<unsigned char>::const_iterator(value + length); 
} 

既然我不希望重寫了整個事情,有沒有一種可移植的方式來創建這些const_iterators?

+2

對不起,您正在利用VS2013中不存在的gcc的實現細節。 const_iterator的定義不在你的控制範圍之內,它不包含任意指針的能力。 –

+0

您可以將類型從'std :: vector :: iterator'更改爲'const unsigned char *'或自定義迭代器? – user1781290

+0

或者,如果'value'總是來自一個向量,你能改變它爲'std :: vector :: iterator value;'? – aschepler

回答

5

由於沒有要求可以從指向基礎值類型的指針構造(const_)iterator,因此沒有可移植的方法來執行您正在嘗試的操作。 libstdC++碰巧提供了這樣的構造函數,但VS標準庫實現不提供。相反,其(const_)iterator構造函數接受一個指向底層值類型的指針和一個指向容器本身的指針,該指針用於在調試構建過程中執行額外的驗證。

最簡單的解決方案是用unsigned char const *替換std::vector<unsigned char>::const_iterator。原始指針位於RandomAccessIterator類別中,與vector::(const_)iterator相同。

unsigned char const *foo::begin() { 
    return value; 
} 

unsigned char const *foo::end() { 
    return value + length; 
} 

如果您需要迭代器是一個類類型,那麼你需要創建一個自定義的迭代器。雖然這可以從頭開始,但使用Boost.IteratorFacade會更容易,它將提供一大堆構建自定義迭代器所需的樣板。

#include <boost/iterator/iterator_facade.hpp> 

struct const_foo_iterator : boost::iterator_facade<const_foo_iterator, 
                unsigned char const, 
                boost::random_access_traversal_tag> 
{ 
    const_foo_iterator() = default; 
    const_foo_iterator(unsigned char const *iter) : iter(iter) {} 
private: 
    friend class boost::iterator_core_access; 

    void increment() { ++iter; } 
    void decrement() { --iter; } 
    void advance(std::ptrdiff_t n) { iter += n; } 

    std::ptrdiff_t distance_to(const_foo_iterator const& other) const 
    { return iter - other.iter; } 

    bool equal(const_foo_iterator const& other) const 
    { return this->iter == other.iter; } 

    unsigned char const& dereference() const { return *iter; } 
    unsigned char const* iter = nullptr; 
}; 

const_foo_iterator foo::begin() { 
    return value; 
} 

const_foo_iterator foo::end() { 
    return value + length; 
} 

static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::value_type, 
          unsigned char>::value, "value_type"); 
static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::pointer, 
          unsigned char const *>::value, "pointer"); 
static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::iterator_category, 
          std::random_access_iterator_tag>::value, "iterator_category"); 

Live demo

+0

非常感謝,我害怕的是 - 沒有出獄禁卡。這個樣本顯示了我沒有寫的大量代碼中的衝突的基本要素,並希望避免進行大手術。 – user3825850

+0

這個答案再次假定調用代碼是通用的,不足以關心迭代器的實際類型。但是,如果其餘的代碼包含像'for(std :: vector :: const_iterator iter = my_foo.begin(); iter!= my_foo.end(); ++ iter)地點? –

+1

@ChristianHackl是的,這是沒有辦法繞 – Praetorian

2

這取決於begin()end()功能是如何使用的。如果這些功能的調用者實際上期望得到std::vector<unsigned char>::const_iterator(而不是通用性不足以不關心確切類型),那麼您必須搜索不同的解決方案。

與其更改類的接口,您可以嘗試在其實現中實現解決方法。什麼是value指針需要?還有誰能夠訪問它?如果它是一個私有成員,那麼只需用一個實際的std::vector<unsigned char>替換它,然後使用它的data()成員函數(C++ 11)或&value[0](pre C++ 11)將指針傳遞到預期的位置。