[英]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.