以下代码在Microsoft Visual Studio 2015中不起作用:
#include <vector>
#include <list>
#include <iostream>
class ListWithIterator
{
public:
ListWithIterator() : m_iterator(m_list.end()) {}
bool check() const { return m_iterator == m_list.end(); }
private:
typedef std::list<int> list_t;
list_t m_list;
list_t::const_iterator m_iterator;
};
int main(int, char**)
{
std::vector<ListWithIterator> v;
v.resize(1);
if (v[0].check())
{
std::cerr << "Yes" << std::endl;
}
else
{
std::cerr << "No" << std::endl;
}
return 0;
}
MSVC说:
“Debug Assertion Failed”,“Expression:list iterators incompatible”in
check()
function。
g ++在没有任何警告的情况下编译它,但它的工作错误:
$ g++ sample.cpp -g -Og -Wall
$ ./a.out
No
$
我期望输出“是”,因为迭代器由m_list.end()
初始化,但bool check() const { return m_iterator == m_list.end(); }
返回false
。
更新
正确的解决方案:
$ cat sample.cpp
#include <vector>
#include <list>
#include <iostream>
class ListWithIterator
{
public:
ListWithIterator() : m_iterator(m_list.end()) {}
ListWithIterator(const ListWithIterator& from): m_list(from.m_list), m_iterator(m_list.end())
{
}
bool check() const
{
std::cerr << m_list.size() << std::endl;
return m_iterator == m_list.end();
}
private:
typedef std::list<int> list_t;
list_t m_list;
list_t::iterator m_iterator;
};
int main(int, char**)
{
std::vector<ListWithIterator> v;
v.resize(1);
if (v[0].check())
{
std::cerr << "Yes" << std::endl;
}
else
{
std::cerr << "No" << std::endl;
}
return 0;
}
$ g++ sample.cpp -g -Og -Wall
$ ./a.out
0
Yes
$
答案 0 :(得分:4)
您正在编译时没有-std=c++11
标记,并且只有一个pre-C++11 overload:
void resize( size_type count, T value = T() );
如果当前大小小于count,则附加其他元素并使用值副本进行初始化。
现在,默认的复制构造函数复制构造了两个成员数据,因此最终得到了旧的(破坏的)列表的迭代器,稍后compare with an end iterator of a different list instance导致未定义的行为。
在C ++ 11下编译会使定义的行为(将选择使用默认构造函数的重载),但修复损坏的副本语义符合您自己的利益。
答案 1 :(得分:3)
如果您正在触发ListWithIterator
的隐式复制构造函数,那么最终会得到列表的副本和迭代器的副本。但是,复制的迭代器仍然引用原始列表。不同包含的迭代器的比较是未定义的。
我没有看到您的ListWithIterator
会被复制到哪里,但来自MSVC的消息表明 仍然被复制。
答案 2 :(得分:3)
您尚未定义复制构造函数,list::resize
会插入默认构造对象的副本。
此副本包含一个迭代器,它引用原始对象中的列表,而不是副本中的列表。
你需要像使用指针一样小心迭代器。