为什么此代码无法按预期工作?

时间:2017-01-23 21:40:07

标签: c++ list vector iterator

以下代码在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
$

3 个答案:

答案 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会插入默认构造对象的副本。
此副本包含一个迭代器,它引用原始对象中的列表,而不是副本中的列表。

你需要像使用指针一样小心迭代器。