2014-11-04 26 views
3

我想寫的通用函數接收container1[a1, .. , an]並返回container2[convert(a1), .. , convert(an)]。 如果container2std::vector,問題是微不足道的,std::transform正是我想要的。下面的函數可以處理任意container2container1std ::轉換爲任意容器

template<class ToType, class FromType> 
ToType convert(const FromType& from) 
{ 
    std::vector<typename ToType::value_type> tmp; 
    std::transform(from.begin(), from.end(), 
        std::back_inserter(tmp), 
        [](const typename FromType::value_type& f) { 
     return convert<typename ToType::value_type>(f); 
    }); 
    return ToType(tmp.begin(), tmp.end()); 
} 

但它確實增加複製。有誰知道如何做得更好?

+0

爲什麼不使用'ToType TMP;'直接? – Jarod42 2014-11-04 08:48:52

+0

@ Jarod42:並非所有容器都支持'push_back'。 – 2014-11-04 08:50:01

+0

2Jarod42我必須使用std :: back_inserter是std :: transform,以防我想要填充std :: vector。但對於std :: back_inserter我不能填充例如std :: set – 2014-11-04 08:54:48

回答

3

結賬this answerIs it possible to write a C++ template to check for a function's existence?。您可以使用SFINAE檢測目標容器的功能是否存在(如push_backinsert),或者存在容器的插入器(例如inserterback_inserter),並相應地執行此操作。

另一種方法是創建一個假的迭代器:

template <class T, class U> 
struct ConvertIterator { 
    typedef T dest_type; 
    typedef U it_type; 

    ConvertIterator(U&& val) : iterator(std::forward<U>(val)) { 

    } 

    bool operator == (const ConvertIterator &other) const { 
     return iterator == other.iterator; 
    } 

    bool operator != (const ConvertIterator &other) const { 
     return iterator != other.iterator; 
    } 

    dest_type operator *() const { 
     return convert<dest_type>(*iterator); 
    } 

    ConvertIterator<T, U> & operator ++() { 
     ++iterator; 
     return *this; 
    } 

    it_type iterator; 
}; 

然後:

template<class ToType, class FromType> 
ToType convert(const FromType& from) 
{ 
    typedef ConvertIterator<typename ToType::value_type, decltype(from.begin()) > convert_it; 

    return ToType(convert_it(from.begin()), convert_it(from.end())); 
} 
+0

注意:當引用一個答案時,最好直接鏈接答案,或者至少給答案者的名稱作爲參考點,因爲排名是動態的並且可能會改變。我將編輯你的答案,直接指向Xeo的答案(使用底部的「分享」鏈接),因爲它現在是第三個,如果這不是你想指向的那個......那麼你將不得不修復鏈接。 – 2014-11-04 10:18:43

+0

我想將轉換器(無狀態?)模板函數對象傳遞給'ConvertIterator',但這很煩人。嗯。上面沒有遇到問題,它遞歸調用'convert'沒有終止的情況下? – Yakk 2014-11-04 15:54:51

+0

@Yakk:我想你可以在ConvertIterator中添加第三個模板參數,並將其作爲構造函數的第二個參數(對於開始和結束迭代器)。 'typedef ConvertIterator <...,...,decltype(變壓器)> convert_it;'和'convert_it(from.begin(),transformer)'。 對於第二個問題,不,一個轉換函數(顯示的那個)有兩個參數,並且暗示還有另一個採用一個參數並且是非遞歸的來轉換value_type。 – coyotte508 2014-11-04 16:00:34

0

這裏是一個基於函數的轉換迭代器。它具有正向迭代器的所有正確類型定義。我們可以將其升級到支持所有傳入Base迭代器類型的標記屬性的,如果我們選擇:

template< 
    class Base, 
    class F, 
    class R=typename std::result_of<F(decltype(*std::declval<Base const&>()))>::type 
> 
struct convert_iterator: 
    std::iterator<std::forward_iterator_tag,typename std::decay<R>::type> 
{ 
    Base it; 
    F f; 

    template<class It, class Func> 
    convert_iterator(It&&base, Func&&func):it(std::forward<It>(base)), 
    // defaulted stuff: 
    convert_iterator()=default; 
    convert_iterator(convert_iterator const&)=default; 
    convert_iterator(convert_iterator &&)=default; 
    convert_iterator& operator=(convert_iterator const&)=default; 
    convert_iterator& operator=(convert_iterator &&)=default; 

    bool operator==(convert_iterator const&other) const { 
    return it == other.it; 
    } 
    bool operator!=(convert_iterator const&other) const { return !(*this==other); } 

    // a bit overkill, but rvalue and lvalue overrides for these: 
    R operator*() const& { 
    return f(*it); 
    } 
    R operator*() & { 
    return f(*it); 
    } 
    R operator*() const&& { 
    return std::move(f)(*std::move(it)); 
    } 
    R operator*() && { 
    return std::move(f)(*std::move(it)); 
    } 
    // normal pre-increment: 
    convert_iterator& operator++()& { 
    ++it; 
    return *this; 
    } 
    // pre-increment when we are guaranteed not to be used again can be done differently: 
    convert_iterator operator++()&& { 
    return {std::next(std::move(it)), std::forward<F>(f)}; 
    } 
    // block rvalue post-increment like a boss: 
    convert_iterator operator++(int)& { 
    return {it++, f}; 
    } 
}; 

一個輔助函數來創建它們:

template< class Base, class F > 
convert_iterator<typename std::decay<Base>::type,typename std::decay<F>::type> 
make_convert_iterator(Base&& b, F&& f) { return {std::forward<Base>(b), std::forward<F>(f)}; } 

接下來,我創建處理轉換類。專業化讓我們派遣不同的容器和標量:

// for scalars: 
template<class ToType,class=void> 
struct converter { 
    template<class FromType> 
    ToType operator()(FromType&& from)const{ return std::forward<FromType>(from); } 
}; 

// attempt at SFINAE test for container: 
template<class ToContainer> 
struct converter<ToContainer, (void)(
    typename std::iterator_traits< 
    typename std::decay<decltype(std::begin(std::declval<ToContainer&>())>::type 
    >::value_type 
)> 
{ 
    using std::begin; using std::end; 

    using R=std::iterator_traits<typename std::decay<decltype(begin(std::declval<ToContainer&>()))>::type>::value_type; 

    template<class FromType, class T=decltype(*begin(std::declval<FromType>())> 
    ToContainer operator()(FromType&& from) const { 
    auto sub_convert = [](T&& t)->R{ 
     return converter<R>{}(std::forward<T>(t)); 
    }; 
    return { 
     make_convert_iterator(begin(std::forward<From>(from)), sub_convert), 
     make_convert_iterator(end(std::forward<From>(from)), sub_convert) 
    }; 
    }; 
}; 

現在的動作轉換功能是一個班輪:

template<class ToType> 
ToType convert(FromType&& from) 
{ 
    return converter<ToType>{}(std::forward<FromType>(from)); 
}