久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

Q_FOREACH (= foreach) 宏是如何工作的,為什么這么復

How does Q_FOREACH (= foreach) macro work and why is it that complex?(Q_FOREACH (= foreach) 宏是如何工作的,為什么這么復雜?)
本文介紹了Q_FOREACH (= foreach) 宏是如何工作的,為什么這么復雜?的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

在 Qt 中,有一個使用宏 (Q_FOREACH) 實現的 foreach 循環.有不同的實現,取決于編譯器.

In Qt, there is a foreach loop which is implemented using macros (Q_FOREACH). There are different implementations, depending on the compiler.

GCC的定義如下:

#define Q_FOREACH(variable, container)                                
for (QForeachContainer<__typeof__(container)> _container_(container); 
     !_container_.brk && _container_.i != _container_.e;              
     __extension__  ({ ++_container_.brk; ++_container_.i; }))        
    for (variable = *_container_.i;; __extension__ ({--_container_.brk; break;}))

... 使用定義如下的輔助類 QForeachContainer:

... using the helper class QForeachContainer which is defined as follows:

template <typename T>
class QForeachContainer {
public:
    inline QForeachContainer(const T& t) : c(t), brk(0), i(c.begin()), e(c.end()) { }
    const T c;
    int brk;
    typename T::const_iterator i, e;
};

Q_FOREACH 宏中的容器必須是一個 T 類,它至少必須提供一個 T::const_iterator 類型,一個T.begin() 和一個 T.end() 方法,所有 STL 容器以及大多數 Qt 容器(如 QListQVector, QMap, QHash, ...

The container in a Q_FOREACH macro has to be a class T which at least has to provide a T::const_iterator type, a T.begin() and a T.end() method, as do all STL containers as well as most Qt containers like QList, QVector, QMap, QHash, ...

我現在的問題是:這個宏是如何工作的?

有一件事似乎很奇怪:變量在宏定義中只出現一次.所以例如foreach(QString item, list) 有一個 QString item = 但之后沒有 item = 任何時候......變量如何>item 然后在每一步都改變?

One thing seems to be really odd: The variable only appears once in the macro definition. So e.g. foreach(QString item, list) has a QString item = but no item = afterwards at any time... How can the variable item then be changed in each step?

更令人困惑的是Q_FOREACH對于MS VC++編譯器的以下定義:

Even more confusing is the following definition of Q_FOREACH for the MS VC++ compiler:

#define Q_FOREACH(variable,container)                                                         
if(0){}else                                                                                     
for (const QForeachContainerBase &_container_ = qForeachContainerNew(container);                
     qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->condition();       
     ++qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->i)               
    for (variable = *qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->i; 
         qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->brk;           
         --qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->brk)

為什么 true : 0 ?...?這不是總是被評估為 0 嗎?即使之前的條件為真,函數調用qForeachPointer(container)是否執行??

Why true : 0 ? ...? Doesn't this always get evaluated to 0? Is the function call qForeachPointer(container) executed even if the condition before ? is true?

為什么我們需要兩個 for 循環?

And why do we need two for-loops?

如果有人能讓我更清楚一點就好了!

It would be cool if anyone can make things a bit clearer for me!

推薦答案

GCC 版本


GCC 真的很簡單.首先它是這樣使用的:

The GCC version


The GCC one is really quite simple. First of all it is used like this:

Q_FOREACH(x, cont)
{
    // do stuff
}

這將擴展為

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
    }

所以首先:

for (QForeachContainer<__typeof__(cont)> _container_(cont); !_container_.brk && _container_.i != _container_.e; __extension__  ({ ++_container_.brk; ++_container_.i; }))

這是實際的 for 循環.它設置了一個 QForeachContainer 來幫助迭代.brk 變量初始化為 0.然后測試條件:

This is the actual for loop. It sets up a QForeachContainer to help with the iteration. The brk variable is intitialised to 0. Then the condition is tested:

!_container_.brk && _container_.i != _container_.e

brk 為零,所以 !brk 為真,并且大概如果容器有任何元素 i(當前元素)沒有等于 e(最后一個元素).

brk is zero so !brk is true, and presumably if the container has any elements i (the current element) doesn't equal e (the last element) yet.

然后輸入那個外層for的主體,即:

Then the body of that outer for is entered, which is:

for (variable = *_container_.i;; __extension__ ({--_container_.brk; break;}))
{
    // do stuff
}

所以 x 被設置為 *_container_.i 這是迭代所在的當前元素,并且沒有條件所以大概這個循環將永遠持續下去.然后進入循環體,這是我們的代碼,它只是一個注釋,所以它什么都不做.

So x is set to *_container_.i which is the current element the iteration is on, and there is no condition so presumably this loop will continue forever. Then the body of the loop is entered, which is our code, and it's just a comment so it doesn't do anything.

然后進入內循環的增量部分,有意思:

Then the increment part of the inner loop is entered, which is interesting:

__extension__ ({--_container_.brk; break;})

它遞減 brk 所以現在是 -1,并跳出循環(使用 __extension__ 這使得 GCC 不會發出使用 GCC 擴展的警告,就像你現在知道的那樣).

It decrements brk so that's now -1, and breaks out of the loop (with __extension__ which makes GCC not emit warnings for using GCC extensions, like you now know).

然后進入外循環的增量部分:

Then the increment part of the outer loop is entered:

__extension__  ({ ++_container_.brk; ++_container_.i; })

它再次增加 brk 并再次使它為 0,然后 i 增加所以我們到達下一個元素.檢查條件,并且由于 brk 現在是 0 并且 i 大概不等于 e (如果我們有更多元素)過程重復.

which increments brk again and makes it 0 again, and then i is incremented so we get to the next element. The condition is checked, and since brk is now 0 and i presumably doesn't equal e yet (if we have more elements) the process is repeated.

為什么我們要先減少然后增加brk?原因是因為如果我們在代碼體中使用了break,內循環的增量部分將不會被執行,就像這樣:

Why did we decrement and then increment brk like that? The reason is because the increment part of the inner loop will not be executed if we used break in the body of our code, like this:

Q_FOREACH(x, cont)
{
    break;
}

然后brk跳出內循環時仍為0,然后進入外循環的增量部分,自增為1,然后!brk 為假,外循環的條件為假,foreach 將停止.

Then brk would still be 0 when it breaks out of the inner loop, and then the increment part of the outer loop would be entered and increment it to 1, then !brk would be false and the outer loop's condition would evaluate to false, and the foreach would stop.

訣竅是要意識到有兩個 for 循環;外部的生命周期是整個 foreach,而內部的生命周期僅持續一個元素.它是一個無限循環,因為它沒有條件,但它要么被它的增量部分break刪除,要么被break 在您提供的代碼中.這就是為什么 x 看起來像是被分配給了只有一次",但實際上它在外循環的每次迭代中都被分配了.

The trick is to realise that there are two for loops; the outer one's lifetime is the whole foreach, but the inner one only lasts for one element. It would be an infinite loop since it doesn't have a condition, but it is either breaked out of by it's increment part, or by a break in the code you provide it. That's why x looks like it is assigned to "only once" but actually it's assigned to on every iteration of the outer loop.

VS 版本有點復雜,因為它必須解決缺少 GCC 擴展 __typeof__ 和塊表達式的問題,并且它為 (6) 編寫的 VS 版本沒有'沒有 auto 或其他花哨的 C++11 特性.

The VS version is a little more complicated because it has to work around the lack of the GCC extension __typeof__ and block-expressions, and the version of VS it was written for (6) didn't have auto or other fancy C++11 features.

讓我們看一下我們之前使用的擴展示例:

Let's look at an example expansion for what we used earlier:

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 錯誤的解決方法.他們使用 if(0){}else 而不是 if(0){...} 的原因是你不能添加 else 在循環之后,如

The if(0){}else is because VC++ 6 did the scoping of for variables wrong and a variable declared in the initialisation part of a for loop could be used outside the loop. So it's a workaround for a VS bug. The reason they did if(0){}else instead of just if(0){...} is so that you can't add an else after the loop, like

Q_FOREACH(x, cont)
{
    // do stuff
} else {
    // This code is never called
}

其次,我們看一下外層for的初始化:

Second, let's look at the initialisation of the outer for:

const QForeachContainerBase &_container_ = qForeachContainerNew(cont)

QForeachContainerBase 的定義是:

struct QForeachContainerBase {};

qForeachContainerNew的定義是

template <typename T>
inline QForeachContainer<T>
qForeachContainerNew(const T& t) {
    return QForeachContainer<T>(t);
}

QForeachContainer的定義是

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); }
};

所以為了彌補__typeof__(類似于C++11的decltype)的不足,我們不得不使用多態.qForeachContainerNew 函數按值返回 QForeachContainer,但由于 臨時對象的生命周期延長,如果我們將它存儲在一個const QForeachContainer&中,我們可以延長它的生命周期直到外部 for 的結尾(實際上是 if 因為 VC6 的錯誤).我們可以在QForeachContainerBase中存儲一個QForeachContainer,因為前者是后者的子類,我們必須像QForeachContainerBase& 而不是像 QForeachContainerBase 這樣的值以避免切片.

So to make up for the lack of __typeof__ (which analogous to the decltype of C++11) we have to use polymorphism. The qForeachContainerNew function returns a QForeachContainer<T> by value but due to lifetime extension of temporaries, if we store it in a const QForeachContainer&, we can prolong it's lifetime till the end of the outer for (actually the if because of VC6's bug). We can store a QForeachContainer<T> in a QForeachContainerBase because the former is a subclass of the latter, and we have to make it a reference like QForeachContainerBase& instead of a value like QForeachContainerBase to avoid slicing.

那么對于外層for的條件:

qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition(); 

qForeachContainer 的定義是

inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) {
    return static_cast<const QForeachContainer<T> *>(base);
}

qForeachPointer的定義是

template <typename T>
inline T *qForeachPointer(const T &) {
    return 0;
}

這是您可能不知道發生了什么的地方,因為這些功能似乎毫無意義.下面是它們的工作原理以及您需要它們的原因:

This is where you might not be aware of what's going on since these functions seem kind of pointless. Well here's how they work and why you need them:

我們有一個 QForeachContainer<T> 存儲在對 QForeachContainerBase 的引用中,并且無法將其取出(我們可以看到).我們必須以某種方式將其強制轉換為正確的類型,這就是兩個函數的用武之地.但是我們如何知道將其強制轉換為什么類型?

We have a QForeachContainer<T> stored in a reference to a QForeachContainerBase with no way to get it back out (that we can see). We have to cast it to the proper type somehow, and that's where the two functions come in. But how do we know what type to cast it to?

三元運算符 x 的規則?y : zyz 必須是同一類型.我們需要知道容器的類型,所以我們使用 qForeachPointer 函數來做到這一點:

A rule of the ternary operator x ? y : z is that y and z must be of the same type. We need to know the type of the container, so we use the qForeachPointer function to do that:

qForeachPointer(cont)

qForeachPointer的返回類型是T*,所以我們使用模板類型推導來推導容器的類型.

The return type of qForeachPointer is T*, so we use template type deduction to deduce the type of the container.

真的嗎?0 : qForeachPointer(cont) 是為了能夠將正確類型的 NULL 指針傳遞給 qForeachContainer 以便它知道要轉換我們的指針的類型給它.為什么我們為此使用三元運算符而不是僅僅使用 qForeachContainer(&_container_, qForeachPointer(cont))?這是為了避免多次評估 cont.?: 的第二個(實際上是第三個)操作數不會被求值,除非條件是 false,而且由于條件本身是 true,我們可以無需評估即可獲得正確類型的 cont.

The true ? 0 : qForeachPointer(cont) is to be able to pass a NULL pointer of the right type to qForeachContainer so it will know what type to cast the pointer we give it to. Why do we use the ternary operator for this instead of just doing qForeachContainer(&_container_, qForeachPointer(cont))? It's to avoid evaluating cont many times. The second (actually third) operand to ?: is not evaluated unless the condition is false, and since the condition is true itself, we can get the right type of cont without evaluating it.

這樣就解決了,我們使用 qForeachContainer_container_ 轉換為正確的類型.電話是:

So that solves that, and we use qForeachContainer to cast _container_ to the right type. The call is:

qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))

再次定義是

inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) {
    return static_cast<const QForeachContainer<T> *>(base);
}

第二個參數總是 NULL 因為我們做 true ?0 總是計算為 0,我們使用 qForeachPointer 來推導出 T 類型,并使用它來將第一個參數轉換為 QForeachContainerT>* 所以我們可以使用它的成員函數/變量和條件(仍然在外部 for 中):

The second parameter will always be NULL because we do true ? 0 which always evaluates to 0, and we use qForeachPointer to deduce the type T, and use that to cast the first argument to a QForeachContainer<T>* so we can use its member functions/variables with the condition (still in the outer for):

qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition()

并且 condition 返回:

(!brk++ && i != e)

與上面的 GCC 版本相同,只是它在評估后增加 brk.所以 !brk++ 計算結果為 true,然后 brk 增加到 1.

which is the same as the GCC version above except that it increments brk after evaluating it. So !brk++ evaluates to true and then brk is incremented to 1.

然后我們輸入內部的for并開始初始化:

Then we enter the inner for and begin with the initialisation:

x = *qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->i

它只是將變量設置為迭代器 i 指向的內容.

Which just sets the variable to what the iterator i is pointing to.

那么條件:

qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk

由于brk為1,進入循環體,也就是我們的注釋:

Since brk is 1, the body of the loop is entered, which is our comment:

// stuff

然后輸入增量:

--qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk

這將 brk 遞減回 0.然后再次檢查條件:

That decrements brk back to 0. Then the condition is checked again:

qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->brk

并且 brk 為 0,即 false 并退出循環.我們來到外層for的增量部分:

And brk is 0 which is false and the loop is exited. We come to the increment part of the outer for:

++qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->i

然后將 i 增加到下一個元素.然后我們得到條件:

And that increments i to the next element. Then we get to the condition:

qForeachContainer(&_container_, true ? 0 : qForeachPointer(cont))->condition()

檢查 brk 是否為 0(確實如此)并再次將其遞增為 1,如果 i != e 則重復該過程.

Which checks that brk is 0 (which it is) and increments it to 1 again, and the process is repeated if i != e.

這在客戶端代碼中處理 break 與 GCC 版本僅略有不同,因為如果我們使用 break 在我們的代碼,它仍然是 1,并且 condition() 對于外循環將是 false,外循環將 break.

This handles break in client code only a little differently than the GCC version, since brk will not be decremented if we use break in our code and it will still be 1, and the condition() will be false for the outer loop and the outer loop will break.

正如 GManNickG 在評論中所說,這個宏很像 Boost 的 BOOST_FOREACH,你可以閱讀它關于 這里.就這樣吧,希望能幫到你.

And as GManNickG stated in the comments, this macro is a lot like Boost's BOOST_FOREACH which you can read about here. So there you have it, hope that helps you out.

這篇關于Q_FOREACH (= foreach) 宏是如何工作的,為什么這么復雜?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

How can I read and manipulate CSV file data in C++?(如何在 C++ 中讀取和操作 CSV 文件數據?)
In C++ why can#39;t I write a for() loop like this: for( int i = 1, double i2 = 0; (在 C++ 中,為什么我不能像這樣編寫 for() 循環: for( int i = 1, double i2 = 0;)
How does OpenMP handle nested loops?(OpenMP 如何處理嵌套循環?)
Reusing thread in loop c++(在循環 C++ 中重用線程)
Precise thread sleep needed. Max 1ms error(需要精確的線程睡眠.最大 1ms 誤差)
Is there ever a need for a quot;do {...} while ( )quot; loop?(是否需要“do {...} while ()?環形?)
主站蜘蛛池模板: 第四色狠狠| 精品久久中文字幕 | 国产精品观看 | 欧美视频在线看 | 91久久精品一区二区二区 | 亚洲一区综合 | jdav视频在线观看免费 | 亚洲国产精品美女 | 91精品国产91久久久久久最新 | 成人精品一区二区三区 | 人人人人干 | 成年人在线观看视频 | 仙人掌旅馆在线观看 | 国产丝袜一区二区三区免费视频 | 国产精品午夜电影 | 亚洲精品在线免费 | 欧美黄色网络 | 一级毛片免费完整视频 | 日韩一区二区三区在线播放 | 91久久精品国产91久久 | 欧美日韩黄色一级片 | 亚洲黄色片免费观看 | 亚洲国产一区在线 | 激情一区二区三区 | 精品中文字幕视频 | 蜜桃免费一区二区三区 | 伊人春色在线观看 | 国产福利精品一区 | 97色在线观看免费视频 | 久久国产精品色av免费观看 | 国产一区二区三区日韩 | 欧美日韩高清免费 | 一区二区三区视频在线观看 | 成人激情视频免费在线观看 | 亚洲永久免费观看 | 日韩欧美亚洲综合 | 亚洲激情专区 | 黄色在线网站 | 日韩波多野结衣 | 亚洲成人a v | 米奇成人网 |