我刚才被告知,在
{
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&lt;&gt;的有效使用。还有其他人遇到过这个问题吗?
- 编辑: 感谢所有有用的回复,回复人们说它是未定义的行为。 有没有办法替换上面可用的 n = 0 的原始数组分配?
虽然说检查 n = 0 作为例外情况将解决问题(它会)。有许多模式不需要特殊情况(例如上面的原始指针示例),所以可能使用除了std :: vector&lt;&gt;之外的其他东西。需要吗?
答案 0 :(得分:8)
见LWG issue 464。这是一个已知的问题。
C ++ 0x(部分由MSVC 2010实现)通过添加.data()
成员来解决它。
答案 1 :(得分:4)
就C ++标准而言,operator[]
并不保证不会检查,只是(不像at()
)它不能保证检查。
您希望在非检查实现中,&scoped_array[scoped_array.size()]
将导致向量分配的数组内或一端的合法指针。这没有明确保证,但对于给定的实现,您可以通过查看其来源进行验证。对于空向量,可能根本没有分配(作为优化),并且我在标准的vector
部分中没有看到任何定义scoped_array[0]
除表之外的结果的内容68。
从表68开始,您可能会说表达式的结果是&*(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,这对我来说是错误的,无论一旦禁用检查,它是否真的适用于您的实现。
答案 2 :(得分:1)
operator []
返回一个引用,因此必须在未定义的空向量上调用它。
毕竟,当没有物品时,引用应该引用哪个项目? operator []
必须返回空引用或完全无效的引用。这两种情况都会导致未定义的行为。
所以是的,你一直在使用未定义的行为。 Visual Studio在operator []
中进行的非强制性但仍然合理的检查只是揭示了这一事实。
答案 3 :(得分:0)
即使在发布版本中,MVS也会对operator[]
进行范围检查。我不知道它是否符合标准。 (我实际上在他们的实现中发现了调试代码,这使得他们的实现破坏了正确的代码虽然有一个开关可以禁用它。
答案 4 :(得分:0)
如果您希望在此方案中获得更清晰的行为,您可以将a[0]
替换为使用a.at(0)
,如果索引无效,则会使用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
}
}
。
一个实用的解决方案是使用n + 1个条目初始化向量,并限制对0..n-1的访问(正如此代码已经实现的那样)。
{{1}}
答案 5 :(得分:0)
这给我带来了一个有趣的问题,我很快就问了here。在您的情况下,您可以通过以下方式避免使用指针:
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
个字符。迭代缓冲区时,请使用:
for (std::vector<char>::iterator it = buffer.begin(); it != buffer.end(); it++)
而非计数器。
答案 6 :(得分:0)
你能使用迭代器而不是指针吗?
{
std::vector<char> scoped_array (size);
std::vector<char>::iterator pointer = scoped_array.begin();
//do work
} // exception safe deallocation