2011-05-18 80 views
5

這是一個對象的我(剝離)類和實例:爲什麼這個構造函數的重載不能正確解析?

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); 

我希望它可以調用Carray(size_type, const T&, const Allocator&)構造,但事實並非如此。大致上這決定到template<typename InputIterator> Carray(InputIterator, InputIterator, const Allocator&)

我該如何改變以使其按預期工作?我覺得它也很奇怪,因爲打電話給std::vector<int> v(5, 10)工作得很好。如果我看的構造函數的定義,在我的GCC的實現,我覺得這(我改名一些編譯器實現的名字,像__n):

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()); 

    // ... 
}; 

這似乎是相同的。

回答

2

這應該適用於所有迭代器類型(包括指針)和當前標準。

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

說明,迭代器通常必須定義幾種類型,如iterator_category,你可以利用這個和SFINAE優勢來檢測實際迭代器。複雜的是,指針也是迭代器,但沒有定義這些類型(std::iterator_traits提供了專門化),所以上面採用了類似的方法,如果傳入的類型是指針,那麼默認情況下它被視爲迭代器。這種方法可以讓您不必測試整型。

觀看演示:http://www.ideone.com/E9l1T

+0

謝謝,這允許我完全定義我的頭文件,而不依賴於boost或非C++ 03。我當然不會在生產代碼中使用它(boost :: enable_if更容易使用和適用)。 – orlp 2011-05-18 22:28:15

+0

@ nightcracker,不用擔心......這是一個有趣的挑戰...... – Nim 2011-05-18 22:32:13

7

顯式構造函數需要size_t和int。你提供了兩個整數。

int代替InputIterator使模板更好匹配。

如果您仔細觀察標準容器,您會發現它們使用了一些模板元編程來確定InputIterator是否可以是真正的迭代器,或者它是否爲整數類型。然後這重定向到一個不同的結構。

編輯
這裏是做這件事的一種方法:

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()); } 

您也可以使用boost :: type_traits如果編譯器不具備的std :: type_traits。

+1

我將如何解決?而'vector'構造函數也需要一個'size_t'和一個'int',然而在傳入'int,int'時,它仍然會解析爲「正確的」。 – orlp 2011-05-18 20:19:13

+0

@nightcracker - 我的答案還沒有完全準備好...... – 2011-05-18 20:22:37

+1

@nightcracker:'std :: vector'表現得那樣,因爲標準要求它表現得那樣。這是標準對基本核心語言行爲的額外要求。如果你想讓你的類也以同樣的方式行事,你必須採取額外的步驟(就像'std :: vector'一樣)。你可以看一下'std :: vector'的具體實現,看看它是如何完成的。 – AnT 2011-05-18 20:23:54

3

試試這個。這將消除需要考慮的迭代器構造函數,如果兩個整數傳遞:

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


編輯:應對「有什麼辦法只用C++ STL 03和沒有提升?「

我不知道std :: type_traits是否在C++ 03中 - 我總是有可用的boost,所以我只是用它。但你可以試試這個。它會在這個特定的情況下工作,但可能不會有您所需要的普遍性:

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

只有C++ 03 STL和沒有提升有什麼辦法嗎?這並不是說我不想使用boost,但我希望這個頭文件是可移植的,並儘可能避免提升。 – orlp 2011-05-18 20:46:19

+0

@nightcracker:你可以寫你自己的。像'is_integral'這樣的類型特徵對於指定來說相對來說是微不足道的。 – Puppy 2011-05-18 22:26:42

0

第一個構造預計,「值」參數按引用傳遞,而第二個構造預計,第一批2倍的值是按價值傳遞。根據我的經驗,C++對這種區別非常嚴格,嘗試傳遞一個整數變量而不是整數值作爲對象構造函數的第二個參數。

相關問題