这是:
auto& ref1 = *it++;
ref1 = expression; // (1)
前向迭代器所需的语义之一?那么随机访问迭代器呢?
auto& ref1 = it[3];
ref1 = expression; // (2)
根据cppreference
,前向迭代器需要:
// Return reference, which equals to (const) value_type &
*it++ === value_type&
和随机访问迭代器:
it[n] === *(it + n)
这是相同的情况,这意味着在两种情况下你都是解除引用临时(迭代器)。在我的例子中,我的迭代器通过复制存储一个索引,该索引允许访问一个容器,该容器不能通过索引直接访问存储的元素。
工作正常:
*it++ = value;
因为it
的临时副本有句子范围。
但在这种情况下:
type& val = *it++;
val = 3;
我们得到了未定义的行为,因为副本已在第二行中被销毁。
在我的情况下,我有一个QModelIndex
包装器来从/ QAbstractItemModel
获取数据/保存。该模型仅为您提供模型上存储的QVariant
的副本。
我的包装类(value_type
重载operator=
)保存了QModelIndex
的实例(用于操作模型),迭代器包含了该包装器的实例。所以,如果迭代器被破坏,包装器和索引也是如此。
我认为只要行(1)
和(2)
不需要支持就可以解决这两个问题。
注意:我的实现或多或少(简化):
// The value type
struct index
{
QModelIndex qidx;
index& operator=(QVariant const& val)
{
if (qidx.isValid())
qidx.model()->setData(qidx, val);
return *this;
}
};
// Private class actually. The "movements" cannot be done
// over the value type because it will cause, in functions
// returning references to the value type, to increase the chaos.
// So, I make the index points to different model items using
// this class.
struct index_manipulator
{
QModelIndex& qidx;
void move(int rows, int cols)
{
if (qidx.isValid())
qidx = qidx.model()->index(qidx.row() + rows,
qidx.column() + cols);
}
};
struct index_safe_ref
{
mutable index idx;
operator index&() const { return idx; }
};
struct my_row_it
{
index idx;
index_manipulator manip = {idx.qidx};
my_row_it(QAbstractItemModel* m, int col)
: idx(m ? m->index(0, col) : QModelIndex())
{}
index& operator*() const { return idx; }
my_row_it operator++(int) const
{
auto copy = it;
manip.move(1, 0);
return copy;
}
index_safe_ref my_row_it::operator[](difference_type n) const
{
auto it = it + n; // Operator+ is over there.
return { it.idx };
}
};
答案 0 :(得分:1)
存储迭代器(即,返回对其内部内容的引用的迭代器)永远不是有效的前向迭代器。
一般的迭代器必须是CopyConstructible
([iterator.iterators]/2.1,除其他外,它要求迭代器的副本等同于原始的迭代器。它遵循前向迭代器及其副本必须比较相等,[forward.iterators]/6要求两个相等的可解除引用的迭代器a
和b
,*a
和*b
必须绑定到同一个对象,而不能对于存储迭代器感到满意。
如果你需要忽略一个要求,我建议忽略一个说reference
必须是实际引用类型的那个,把你的存储迭代器变成一个代理迭代器。在标准库(vector<bool>::iterator
)中已经建立了这种做法,任何破坏都可能是一个响亮的编译时错误,而不是无声的运行时恶作剧。
答案 1 :(得分:0)
这是关于迭代器的一般声明:
破坏迭代器可能会使先前从该迭代器获得的指针和引用无效。
§24.2.1/ 9 N3337
但是,正如T.C.如果返回对迭代器对象中包含的对象的引用,则other answer指出你的迭代器不能是有效的前向迭代器(或者更严格的东西)。
我看到两个解决方案:按值返回index
对象,或者返回对堆已分配index
对象的引用。
注意,输入迭代器需要支持:
value_type temp = *iterator++; // or *iterator; ++iterator;
// use temp
所以在你的情况下,这必须有效(但应该尽我所能):
index temp = *iterator++;
temp = expression.
这与行(1)
不同,因为上面的代码涉及到value_type的转换(而不是对它的引用)。