C++ Q_FOREACH(=FOREACH)宏是如何工作的,为什么会如此复杂?
在Qt中,有一个C++ Q_FOREACH(=FOREACH)宏是如何工作的,为什么会如此复杂?,c++,qt,macros,foreach,C++,Qt,Macros,Foreach,在Qt中,有一个foreach循环,它是使用宏(Q\u foreach)实现的。根据编译器的不同,有不同的实现 GCC的定义如下所示: #定义Q#u FOREACH(变量、容器)\ 用于(QForeachContainer(集装箱)(集装箱))\ !\u container.brk&&u container.i!=\u container.e\ __扩展名(扩展名({++u容器{.brk;++u容器{.i;}))\ for(变量=*_容器uu.i;u扩展名uuu({--_容器uu.brk;bre
foreach
循环,它是使用宏(Q\u foreach
)实现的。根据编译器的不同,有不同的实现
GCC的定义如下所示:
#定义Q#u FOREACH(变量、容器)\
用于(QForeachContainer(集装箱)(集装箱))\
!\u container.brk&&u container.i!=\u container.e\
__扩展名(扩展名({++u容器{.brk;++u容器{.i;}))\
for(变量=*_容器uu.i;u扩展名uuu({--_容器uu.brk;break;}))
。。。使用助手类QForeachContainer
,其定义如下:
模板
QForeachContainer类{
公众:
内联QForeachContainer(const T&T):c(T),brk(0),i(c.begin()),e(c.end()){}
常数tc;
int brk;
类型名T::常量迭代器i,e;
};
Q\u FOREACH
宏中的容器必须是一个类T
,它至少必须提供一个T::const\u迭代器
类型、一个T.begin()
和一个T.end()
方法,所有STL容器以及大多数Qt容器,如QList
、QVector
、QMap
,QHash
我现在的问题是:这个宏是如何工作的?
有一件事似乎很奇怪:变量在宏定义中只出现一次。例如,foreach(QString项,列表)
有一个QString项=
,但之后任何时候都没有item=
。。。如何在每个步骤中更改变量项
更令人困惑的是MS VC++编译器的Q_FOREACH
的以下定义:
#定义Q#u FOREACH(变量、容器)\
如果(0){}else\
对于(const QForeachContainerBase&_container_u=qForeachContainerNew(container))\
qForeachContainer(&_container,true?0:qForeachPointer(container))->condition()\
++qForeachContainer(&_container,true?0:qForeachPointer(container))->i)\
对于(变量=*qForeachContainer(&_container_u2;,true?0:qForeachPointer(container))->i\
qForeachContainer(&_container,true?0:qForeachPointer(container))->brk\
--qForeachContainer(&_container,true?0:qForeachPointer(container))->brk)
为什么true:0代码>?这不总是被计算为0
?即使之前的条件为true,函数调用qForeachPointer(container)
是否也会执行
为什么我们需要两个for循环
如果有人能让我更清楚一点,那就太好了 GCC版本
GCC的方法非常简单。首先,它是这样使用的:
Q_FOREACH(x, cont)
{
// do stuff
}
Q_FOREACH(x, cont)
{
break;
}
这将扩展到
for (QForeachContainer<__typeof__(cont)> _container_(cont); !_container_.brk && _container_.i != _container_.e; __extension__ ({ ++_container_.brk; ++_container_.i; }))
for (x = *_container_.i;; __extension__ ({--_container_.brk; break;}))
{
// do stuff
}
brk
为零,因此!brk
为真,假设容器中有任何元素i
(当前元素)还不等于e
(最后一个元素)
然后输入
的外部主体,即:
for (variable = *_container_.i;; __extension__ ({--_container_.brk; break;}))
{
// do stuff
}
因此,x
被设置为*\u container\ui
,这是迭代所使用的当前元素,并且没有任何条件,因此这个循环可能会永远继续。然后进入循环体,这是我们的代码,它只是一个注释,所以它什么都不做
然后输入内部循环的增量部分,这很有趣:
__extension__ ({--_container_.brk; break;})
它递减brk
,因此现在为-1,并中断循环(使用\uuuuuu扩展\uuuuuuu
,这使得GCC不会发出使用GCC扩展的警告,就像您现在知道的那样)
然后输入外循环的增量部分:
__extension__ ({ ++_container_.brk; ++_container_.i; })
--qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk
它再次递增brk
并使其再次为0,然后i
递增,因此我们进入下一个元素。检查该条件,并且由于brk
现在为0,并且i
可能不等于e
(如果我们有更多元素),该过程将重复
为什么我们要这样递减然后递增brk
?原因是,如果我们在代码体中使用break
,则不会执行内部循环的增量部分,如下所示:
Q_FOREACH(x, cont)
{
// do stuff
}
Q_FOREACH(x, cont)
{
break;
}
然后,brk
当它从内部循环中断时仍然是0,然后输入外部循环的增量部分并将其增量为1,然后!brk
将为false,外部循环的条件将计算为false,foreach将停止
诀窍是认识到有两个for
循环;外在的生命是整个生命,而内在的生命只持续一个元素。它将是一个无限循环,因为它没有条件,但它或者是由它的增量部分,或者是由您提供它的代码中的一个break
导出的。这就是为什么x
看起来像是分配给“仅一次”,但实际上它是在外部循环的每次迭代中分配给的
VS版本
VS版本稍微复杂一些,因为它必须解决缺少GCC扩展名\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
让我们看一下前面使用的扩展示例:
if(0){}else
for (const QForeachContainerBase &_container_ = qForeachContainerNew(cont); qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition(); ++qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->i)
for (x = *qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->i; qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk; --qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk)
{
// stuff
}
if(0){}else
是因为VC++6对for
变量的作用域进行了错误的操作,并且在for
循环的初始化部分声明的变量可以在循环外部使用。所以这是一个VS bug的解决方案。如果(0){}else
而不是仅仅是if(0){…}
,他们这样做的原因是
const QForeachContainerBase &_container_ = qForeachContainerNew(cont)
struct QForeachContainerBase {};
template <typename T>
inline QForeachContainer<T>
qForeachContainerNew(const T& t) {
return QForeachContainer<T>(t);
}
template <typename T>
class QForeachContainer : public QForeachContainerBase {
public:
inline QForeachContainer(const T& t): c(t), brk(0), i(c.begin()), e(c.end()){};
const T c;
mutable int brk;
mutable typename T::const_iterator i, e;
inline bool condition() const { return (!brk++ && i != e); }
};
qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition();
inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) {
return static_cast<const QForeachContainer<T> *>(base);
}
template <typename T>
inline T *qForeachPointer(const T &) {
return 0;
}
qForeachPointer(cont)
qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))
inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) {
return static_cast<const QForeachContainer<T> *>(base);
}
qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition()
(!brk++ && i != e)
x = *qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->i
qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk
// stuff
--qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk
qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk
++qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->i
qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition()