簡體   English   中英

在空std :: vector上使用operator []

[英]Using operator[] on empty std::vector

我剛才被告知, 在c ++中使用std :: vector作為異常安全動態數組而不是分配原始數組是常見的...例如

{
    std::vector<char> scoped_array (size);
    char* pointer = &scoped_array[0];

    //do work

} // exception safe deallocation

我已多次使用此約定沒有問題,但是我最近將一些代碼移植到Win32 VisualStudio2010(之前它只在MacOS / Linux上)並且我的單元測試正在破壞(stdlib拋出一個斷言)當矢量大小恰好是零

我知道寫一個這樣的數組會有問題,但是這個假設打破了這個解決方案作為原始指針的替代。 考慮以下函數, n = 0

void foo (int n) {
   char* raw_array = new char[n];
   char* pointer = raw_array;
   file.read ( pointer , n );
   for (int i = 0; i < n; ++i) {
      //do something
   }
   delete[] raw_array;
}

雖然可以說是多余的,但上面的代碼完全合法(我相信),而下面的代碼將在VisualStudio2010上拋出一個斷言

void foo (int n) {
   std::vector<char> scoped_array (n);
   char* pointer = &scoped_array[0];
   file.read ( pointer , n );
   for (int i = 0; i < n; ++i) {
  //do something
   }
}

我一直在使用未定義的行為嗎? 我的印象是operator []沒有錯誤檢查,這是std :: vector <>的有效使用。 還有其他人遇到過這個問題嗎?

--edit:感謝所有有用的回復,回復人們說它是未定義的行為。 有沒有辦法替換上面的原始數組分配n = 0

雖然說檢查n = 0作為例外情況將解決問題(它會)。 有許多模式不需要特殊情況(例如上面的原始指針示例),所以可能需要使用std :: vector <>以外的東西嗎?

LWG問題464 這是一個已知的問題。 C ++ 0x(部分由MSVC 2010實現)通過添加.data()成員來解決它。

就C ++標准而言,不保證operator[]不會檢查,它只是(與at()不同at()它不能保證檢查。

您期望在非檢查實現中, &scoped_array[scoped_array.size()]將導致合法指針在向量分配的數組內或一端到另一端。 這沒有明確保證,但對於給定的實現,您可以通過查看其來源進行驗證。 對於空向量,可能根本沒有分配(作為優化),並且我沒有在標准的vector部分中看到任何定義除了表68之外的scoped_array[0]的結果的內容。

從表68開始,您可能會說表達式的結果是&*(a.begin() + 0) ,它非法取消引用了一個&*(a.begin() + 0)迭代器。 如果你的實現的向量迭代器只是一個指針,那么你可能會逃避這個 - 如果不是你可能沒有,顯然你的不是。

我忘記了關於是否&* ,對於一個不能被解除引用的指針,是否是無操作的結果。 IIRC從標准(某些地方有些含糊不清)不清楚,這引起了修改標准以使其明確合法的要求。 這表明它確實可以在所有或大多數已知的實現上工作。

我個人不會依賴這個,我不會禁用檢查。 我會重寫你的代碼:

char* pointer = (scoped_array.size() > 0) ? &scoped_array[0] : 0;

或者在這種情況下:

char* pointer = (n > 0) ? &scoped_array[0] : 0;

我只是看錯了使用向量的索引n而不知道大小至少為n + 1,而不管它是否在您禁用檢查后實際工作。

operator []返回一個引用,因此必須取消定義在空向量上調用它。

畢竟,當沒有項目時,引用應該引用哪個項目? operator []必須返回null引用或完全無效的引用。 這兩者都會導致未定義的行為。

所以是的,你一直在使用未定義的行為。 Visual Studio在operator []的非強制性但仍然是一致的檢查才揭示了這一事實。

即使在發布版本中,MVS也會對operator[]范圍檢查。 我不知道它是否符合標准。 (我實際上在他們的實現中發現了調試代碼,這使得他們的實現破壞了正確的代碼 雖然有一個開關來禁用它。

如果你想在這種情況下獲得更清晰的行為,可以使用a.at(0)替換a[0]的使用,如果索引無效則拋出。

一個實用的解決方案是使用n + 1個條目的init向量並約束對0..n-1的訪問(正如此代碼已經做到的)。

void foo (int n) {
   std::vector<char> scoped_array (n+1);
   char* pointer = &scoped_array[0];
   file.read ( pointer , n );
   for (int i = 0; i < n; ++i) {
  //do something
   }
}

這給我帶來了一個有趣的問題,我在這里迅速提出了這個問題 在您的情況下,您可以通過以下方式避免使用指針:

template<class InputIterator, class OutputIterator>
OutputIterator copy_n( InputIterator first, InputIterator last, OutputIterator result, std::size_t n)
{
    for ( std::size_t i = 0; i < n; i++ ) {
        if (first == last)
            break;
        else
            *result++ = *first++;
    }
    return result;
}

std::ifstream file("path_to_file");
std::vector<char> buffer(n);
copy_n(std::istream_iterator<char>(file), 
       std::istream_iterator<char>(),
       std::back_insert_iterator<vector<char> >(buffer),
       n);

這將一次將文件的內容復制到緩沖區n chars。 迭代緩沖區時,使用:

for (std::vector<char>::iterator it = buffer.begin(); it != buffer.end(); it++)

而不是一個櫃台。

你可以使用迭代器而不是指針嗎?

{
    std::vector<char> scoped_array (size);
    std::vector<char>::iterator pointer = scoped_array.begin();

    //do work

} // exception safe deallocation

暫無
暫無

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

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