2017-08-09 29 views
7

Basit bir std::vector uygularım.Derleyici, "vector :: insert" in iki varyasyonu arasında nasıl ayrım yapar?

template <typename T, typename Allocator> 
typename Vector<T, Allocator>::iterator 
Vector<T, Allocator>::insert(const_iterator pos, size_type count, const T& value) 
{ 
    checkIterator(pos); 
    auto p = const_cast<iterator>(pos); 
    if (count == 0) { 
     return p; 
    } 
    for (size_type i = 0; i < count; ++i) { 
     p = insert(p, value); 
    } 
    return p; 
} 

template <typename T, typename Allocator> 
template <typename InputIt> 
typename Vector<T, Allocator>::iterator 
Vector<T, Allocator>::insert(const_iterator pos, InputIt first, InputIt last) 
{ 
    checkIterator(pos); 
    auto p = const_cast<iterator>(pos); 
    if (first == last) { 
     return p; 
    } 
    for (auto iter = first; iter != last; ++iter) { 
     p = insert(p, *iter); 
     ++p; 
    } 
    return p - (last-first); 
} 

Ama önce insert fonksiyonunu kullanmak istediğinizde, derleyici ikincisi çağırır: İki insert fonksiyonlardır

derleyici nasıl kodumu değiştirmeye ikinci işlevi seçer ve Neden
Vector<int> vi = {1, 2, 3}; 
vi.insert(vi.begin(), 3, 4); // get compile error, using insert(const_iterator pos, InputIt first, InputIt last). 

doğru yapmak için?

+2

'std :: vektör'ünün belirtimi, bu durumda" derleyici sihirbazına "dayanır:" Çıktı türü "ifadesi," InputIterator "gereklilikleriyle uyumlu olmadığı sürece seçilmez. Kavramlar yaşadığında, bunu kendi kodunuzda kolayca ifade edebileceğinizi düşünüyorum. O zaman tam olarak ne yapabileceğinizden emin değilim. –

+0

@ M.M Neden düzenli kullanıcı meta programlaması + sfinae yerine derleyici sihirbazını kullanıyor? –

+0

Bağımsız değişkenlerden birini değiştirerek, örneğin, "boyut_türü" için bir "U" sözcüğünü ekleyerek veya bir cast eklemeyi açıkça ekleyerek farklı bir tür değiştirin. – o11c

cevap

7

Ne yazık ki, bunu yapmak tamamen doğru bir sorundur. Ancak, makul bir şey yapabilirsiniz (ve bu durumda çalışacaktır). Temel olarak, sonuçta ortaya çıkan türün InputIt aslında giriş yineleyici gereksinimlerini karşılayıp karşılamadığına bağlı olarak ikinci aşırı yüklenmeyi etkinleştirmeniz gerekir. Giriş yineleyici gereksinimleri için bir liste var: http://en.cppreference.com/w/cpp/concept/InputIterator. Ancak, bu durumu ve bizim için en yaygın durumları çözecek bir konuya odaklanacağız. Yani, InputIt türünün aslında doğru bir operator* olduğunu doğrularız. Bunun için bir özelliği inşa etmek void_t hile kullanın:

template <class ... T> using void_t = void; 

template <class T, class = void> 
struct has_iterator_deref : std::false_type {}; 

template <class T> 
struct has_iterator_deref<T, std::enable_if_t< 
    std::is_same<typename std::iterator_traits<T>::reference, 
       decltype(*std::declval<T>())>::value>> : std::true_type {}; 

uzun lafın kısası, bu yapı T örneğidir * ile indirgenmedikleri ve iterator_traits<T>::reference aynı tip verir edilebilir olmasını sağlayacaktır ki. Eğer oynak hissediyorsanız

template <typename T, typename Allocator> 
template <typename InputIt, class = enable_if_t<has_iterator_deref<T>::value>> 
typename Vector<T, Allocator>::iterator 
Vector<T, Allocator>::insert(const_iterator pos, InputIt first, InputIt last) 
... 

, sen aslında Girdi Yineleyici gereksinimlerinin tüm liste üzerinden gidebilir ve bildiğim kadarıyla gördüğünüz gibi, inşa: Bu işlem tamamlandıktan sonra, artık ikinci aşırı sfinae için kullanabilir Her birinin mevcut olup olmadığını algılayan bir özellik ve son olarak, InputIt'in Giriş Yineleyici kavramını karşıladığından emin olmak için tam olarak doğru algılama yapmak için bağlantıyı kullanın. Gerçi bir acıdır. ki bunu yapmadıkları için, bu int tür şeyleri ortadan SFINAE olacak

#include <iterator> 

template<typename It> 
using iterator_category_t = typename std::iterator_traits<It>::iterator_category; 

template<typename T> 
class container 
{ 
public: 
    template <typename InputIt, typename ItCat = iterator_category_t<InputIt>> 
    iterator insert(const_iterator pos, InputIt first, InputIt last); 
    // the rest 

:

+0

@ 1201ProgramAlarm Sorunun ilk satırı: "Ben basit bir std :: vektör uygulayarak yapıyorum" –

+0

Sadece bir hata aldım diyor ki: "struct std :: type 'adlı tip yok enable_if "yanlış bir şey var 'is_same <...>' Bence. – Sam

+0

Bu çalışır! Ama ben iter = vi.insert (vi.cend(), tmp.begin(), tmp.end()) 'kullandığımda, derleyici 'insert'i (const_iterator pos, size_type sayımı, const T & value)', çağırır. Yapacak daha çok işim var. :) – Sam

1

A bunu yapmak için oldukça basit bir yolu It şuna benzer olabilir <iterator>

dan iterator_traits::iterator_category temel almaktır herhangi bir yineleyici kategorisini yerine getirin.

Bu, farklı iterator kategorileri için farklı algoritmalara sahipseniz, uygulamanın içinde ItCat{}'dan gönderebileceğiniz ek avantajlara sahiptir.

İlgili konular