2011-05-18 16 views
5

Bu, bir nesnenin benim (soyulmuş) sınıf ve örnekleme geçerli:Bu kurucu neden yanlış bir şekilde aşırı yükleniyor?

template <typename T, typename Allocator = std::allocator<T> > 
class Carray { 
    typedef typename Allocator::size_type size_type; 

    // ... 

    explicit Carray(size_type n, const T& value, const Allocator& alloc = Allocator()) { 
     // ... 
    } 

    template<typename InputIterator> 
    Carray(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) { 
     // ... 
    } 

    // ... 
} 

Carray<int> array(5, 10); 

Bu Carray(size_type, const T&, const Allocator&) kurucusunu çağırmak için beklenebilir ancak öyle değil. Anlaşılan bu, template<typename InputIterator> Carray(InputIterator, InputIterator, const Allocator&) için karar verir.

Bu çalışmayı amaçlandığı şekilde yapmak için ne değiştirmeliyim? Bunu da garip buluyorum çünkü std::vector<int> v(5, 10) numaralı telefona yapılan çağrı mükemmel bir şekilde çalışıyor. Ben GCC en uygulanmasında inşaatçı tanımına bakacak olursak (Ben __n gibi bazı derleyici-uygulama adları değiştirildi) bu bulmak: görünüyor

template<typename T, typename A = std::allocator<T> > 
class vector { 
    typedef size_t size_type; 
    typedef T value_type; 
    typedef A allocator_type; 

    // ... 

    explicit vector(size_type n, const value_type& value = value_type(), const allocator_type& a = allocator_type()); 

    template<typename InputIterator> 
    vector(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()); 

    // ... 
}; 

aynı olması.

cevap

2

Bu, tüm yineleyici türleri (işaretçiler dahil) ve geçerli standart ile çalışmalıdır.

#include <iostream> 
#include <iterator> 
#include <vector> 

// uses sfinae to determine if the passed in type is indeed an iterator 
template <typename T> 
struct is_iterator_impl 
{ 
    typedef char yes[1]; 
    typedef char no[2]; 

    template <typename C> 
    static yes& _test(typename C::iterator_category*); 

    template <typename> 
    static no& _test(...); 

    static const bool value = sizeof(_test<T>(0)) == sizeof(yes); 
}; 

template <typename T, bool check = is_iterator_impl<T>::value> 
struct is_iterator 
{ 
    typedef void type; 
}; 

template <typename T> 
struct is_iterator<T, false> 
{ 
}; 

template <typename T> 
struct is_iterator<T*, false> 
{ 
    typedef void type; 
}; 

template <typename T> 
struct foo 
{ 
    explicit foo(std::size_t n, const T& value) 
    { 
    std::cout << "foo:size_t" << std::endl; 
    } 

    template<typename InputIterator> 
    foo(InputIterator first, InputIterator last, typename is_iterator<InputIterator>::type* dummy = 0) 
    { 
    std::cout << "foo::iterator" << std::endl; 
    } 
}; 

int main(void) 
{ 
    // should not cause a problem 
    foo<int> f(1, 2); 

    // using iterators is okay 
    typedef std::vector<int> vec; 
    vec v; 
    foo<int> b(v.begin(), v.end()); 

    // using raw pointers - is okay 
    char bar[] = {'a', 'b', 'c'}; 
    foo<char> c(bar, bar + sizeof(bar)); 
} 

açıklanması, bir yineleyici normalde iterator_category olarak çeşitli tanımlar gerekir ve gerçek yineleyicinızı algılamak için bu ve sfinae yararlanabilir.Complication, işaretçiler de yineleyicilerdir, ancak bu tür tanımlanmış bir tür yok (bir şey için std::iterator_traits bir uzmanlık sağlar), böylece geçmiş tür bir işaretçi ise, yukarıdaki benzer bir yaklaşım alır, varsayılan olarak varsayılan olarak kabul edilir. yineleyici. Bu yaklaşım, entegre tipler için test etmeniz gerektiğinden tasarruf etmenizi sağlar.

Demo'ya bakın: http://www.ideone.com/E9l1T

+0

Teşekkür ederim, bu sayede ya başlığımı ya da C++ 03'e dayanmadan başlığımı tam olarak tanımlamamı sağlıyor. Bunu üretim kodunda kullanmam (elbette boost :: enable_if'in kullanılması ve uygun olması daha kolay). – orlp

+0

@nightcracker, endişelenme ... ilginç bir sorun oldu ... – Nim

7

Açık yapıcı, size_t ve int. İki tane sağladınız. InputIterator için int değiştiriliyorsa, şablon daha iyi eşleşmeyi sağlar.

Standart kaplarda daha yakından bakarsanız, InputIterator öğesinin gerçek yineleyici mi yoksa tamsayı türünde mi olduğunu belirlemek için bir şablon meta programlama kullandıklarını görürsünüz. Bu daha sonra farklı bir yapıya yönlendirir.

template<class _InputIterator> 
    vector(_InputIterator _First, _InputIterator _Last, 
     const allocator_type& _Allocator = allocator_type()) 
    : _MyAllocator(_Allocator), _MyBuffer(nullptr), _MySize(0), _MyCapacity(0) 
    { _Construct(_First, _Last, typename std::is_integral<_InputIterator>::type()); } 

private: 
    template<class _IntegralT> 
    void _Construct(_IntegralT _Count, _IntegralT _Value, std::true_type /* is_integral */) 
    { _ConstructByCount(static_cast<size_type>(_Count), _Value); } 

    template<class _IteratorT> 
    void _Construct(_IteratorT _First, _IteratorT _Last, std::false_type /* !is_integral */) 
    { _Construct(_First, _Last, typename std::iterator_traits<_IteratorT>::iterator_category()); } 

derleyici std :: type_traits yoksa da artırmak :: type_traits kullanabilirsiniz:

Düzenleme
İşte bunu yapmanın bir yoludur.

+1

Bunu nasıl çözerim? Ve 'vektör' yapıcısı bir 'size_t 've bir' int 'de beklerken, int' int 'ise hala' doğru olanı 'çözecektir. – orlp

+0

@nightcracker - Cevabım tam olarak hazır değildi ... –

+1

@nightcracker: 'std :: vector' bu şekilde davranıyor çünkü standart bu şekilde davranmasını gerektiriyor. Temel çekirdek dil davranışının üstünde standart tarafından dayatılan ek bir zorunluluktur. Sınıfınızın da aynı şekilde davranmasını istiyorsanız, ekstra adımlar atmalısınız (tıpkı std :: vector' gibi). Orada nasıl yapıldığını görmek için std :: vector'nun belirli bir uygulamasına bakabilirsiniz. – AnT

3

Bunu deneyin. İki int geçirilen eğer dikkate alınmaz yineleyici yapıcı ortadan kaldıracaktır: Referans

template<typename InputIterator> 
Carray(InputIterator first, InputIterator last, 
    const Allocator& alloc = Allocator(), 
    typename boost::disable_if<boost::is_integral<InputIterator> >::type* dummy = 0) { 
} 

: http://www.boost.org/doc/libs/1_46_1/libs/utility/enable_if.html


DÜZENLEME: yanıt "sadece C++ 03 STL ve herhangi bir yolu var mı etmek takviye olmadan? "

std :: type_traits öğesinin C++ 03 olup olmadığını bilmiyorum - her zaman kullanılabilir durumdayım, bu yüzden kullanıyorum. Ama bunu deneyebilirsin. Bu özel durumda çalışacaktır ancak size gereken genelliği olamaz:

template <class T> class NotInt { typedef void* type; }; 
template <> class NotInt<int> { }; 

template <typename T, typename Allocator = std::allocator<T> > 
class Carray { 
    ... 
    template<typename InputIterator> 
    Carray(InputIterator first, InputIterator last, 
     const Allocator& alloc = Allocator(), 
     typename NotInt<InputIterator>::type t = 0) { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 
}; 
+0

Sadece C++ 03 STL ile ve herhangi bir destek olmadan herhangi bir yolu var mı? Destek kullanmak istemediğimden değil, ancak bu üstbilgi dosyasının taşınabilir olmasını ve mümkünse desteklemekten kaçınmasını istiyorum. – orlp

+0

@nightcracker: Sadece kendiniz yazabilirsiniz. 'Is_integral' gibi yazım karakterleri belirtmek için nispeten önemsizdir. – Puppy

0

birinci yapıcı ikinci kurucu ilk 2 değerleri olmasını bekler iken 'değer' argümanı, referans olarak geçirilmesini bekliyor değere göre geçirilir. Benim deneyimime göre, C++ bu ayrım konusunda oldukça katıdır, nesne yapıcınıza 2. argüman olarak bir tamsayı değişkeni yerine bir tamsayı değişkeni geçirmeyi deneyin.