簡體   English   中英

選擇c ++中的哪個構造函數

[英]choose which constructor in c++

我正在通過構建我自己的vector版本來實踐C ++,名為“Vector”。 我有兩個構造函數,填充構造函數和范圍構造函數。 他們的聲明如下:

template <typename Type> 
class Vector {
public:
    // fill constructor
    Vector(size_t num, const Type& cont);

    // range constructor
    template<typename InputIterator> Vector(InputIterator first, InputIterator last);

    /* 
    other members
    ......
    */
}

填充構造函數用num的num填充容器; 並且范圍構造函數將[first,last]范圍內的每個值復制到容器中。 它們應該與STL向量的兩個構造函數相同。

他們的定義如下:

//fill constructor 
template <typename Type> 
Vector<Type>::Vector(size_t num, const Type& cont){
    content = new Type[num];
    for (int i = 0; i < num; i ++)
        content[i] = cont;
    contentSize = contentCapacity = num;
}

// range constructor
template <typename Type> 
template<typename InputIterator>
Vector<Type>::Vector(InputIterator first, InputIterator last){
    this->content = new Type[last - first];

    int i = 0;
    for (InputIterator iitr = first; iitr != last; iitr ++, i ++)
    *(content + i) = *iitr;

    this->contentSize = this->contentCapacity = i;
}

但是,當我嘗試使用它們時,我有區別它們的問題。 例如:

Vector<int> v1(3, 5);

使用這行代碼,我打算創建一個包含三個元素的Vector,每個元素都是5.但是編譯器用於范圍構造函數,將“3”和“5”都視為“InputIterator”的實例,這沒有任何意外,會導致錯誤。

當然,如果我將代碼更改為:

Vector<int> v1(size_t(3), 5);

一切都很好,填充構造函數被調用。 但這顯然不直觀且用戶友好。

那么,有沒有一種方法可以直觀地使用填充構造函數?

您可以使用std::enable_if (如果不使用C ++ 11,則使用boost::enable_if )來消除構造函數的歧義。

#include <iostream>
#include <type_traits>
#include <vector>
using namespace std;


template <typename Type> 
class Vector {
public:
    // fill constructor
    Vector(size_t num, const Type& cont)
    { 
        cout << "Fill constructor" << endl;
    }

    // range constructor
    template<typename InputIterator> Vector(InputIterator first, InputIterator last,
        typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
    { 
        cout << "Range constructor" << endl;
    }

};

int main()
{
    Vector<int> v1(3, 5);
    std::vector<int> v2(3, 5);
    Vector<int> v3(v2.begin(), v2.end());
}


上面的程序應首先通過檢查類型是否為整數類型(因此不是迭代器)來調用fill構造函數。


順便說一下,在你的范圍構造函數的實現中,你應該使用std::distance(first, last)而不是last - first 在迭代器上顯式使用-運算符會限制您使用RandomAccessIterator類型,但是您希望支持最常用的Iterator類型的InputIterator

即使是std::vector似乎也有這個問題。

std::vector<int> v2(2,3);

template<class _Iter>
        vector(_Iter _First, _Iter _Last)

在Visual C ++中,即使它應該更接近非模板化的情況..

編輯:上面的功能(正確)將結構委托給下面的一個。 我完全失去了..

template<class _Iter>
        void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag)

編輯#2 AH!:

不知何故,下面的函數標識了要調用哪個版本的構造函數。

template<class _Iter> inline
    typename iterator_traits<_Iter>::iterator_category
        _Iter_cat(const _Iter&)
    {   // return category from iterator argument
    typename iterator_traits<_Iter>::iterator_category _Cat;
    return (_Cat);
    }

上面顯示的_Construct函數在第三個變量上有(至少)2個版本的重載,這是由上面的_Iter_cat函數返回的標記。 根據此類別的類型,選擇_Construct的正確重載。

最終編輯: iterator_traits<_Iter>是一個似乎為許多不同的常見變種模板化的類,每個類都返回適當的“類別”類型

解決方案 :看起來第一個爭論類型的模板特化是std庫如何在MS VC ++的情況下處理這種混亂的情況(原始值類型)。 也許你可以調查並跟風?

問題出現了(我認為),因為對於原始值類型, Typesize_t變量是相似的,因此選擇具有兩個相同類型的模板版本。

標准庫實現面臨的問題是相同的。 有幾種方法可以解決它。

  • 您可以為所有整數類型(代替第一個參數)精心提供非模板重載構造函數。

  • 您可以使用基於SFINAE的技術(如enable_if )來確保未為整數參數選擇范圍構造函數。

  • 在檢測到整數參數(通過使用is_integral )並將控制重定向到正確的構造代碼之后,您可以在運行時(使用if )分支范圍構造函數。 分支條件將是編譯時值,這意味着編譯器可能會在編譯時減少代碼。

  • 您可以直接查看您的標准庫實現版本並查看它們是如何實現的(盡管從抽象C ++語言的角度來看,它們的方法不需要是可移植的和/或有效的)。

這種模糊性給早期的圖書館實施者帶來了問題。 它被稱為“做正確的事”效果。 據我所知,你需要SFINAE才能解決它...它可能是該技術的首批應用之一。 (有些編譯器欺騙並破解了他們的重載決策內部,直到在核心語言中找到解決方案。)

此問題的標准規范是C ++ 98和C ++ 03之間的主要區別之一。 從C ++ 11,§23.2.3:

14對於本條款和第21條中定義的每個序列容器:

- 如果是構造函數

  template <class InputIterator> X(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()) 

使用不符合輸入迭代器條件的InputIterator類型調用,然后構造函數不應參與重載決策。

15實現確定類型不能是輸入迭代器的程度是未指定的,除非作為最小整數類型不符合輸入迭代器的條件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM