C++ Q_FOREACH(=FOREACH)宏是如何工作的,为什么会如此复杂?

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

在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;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()