2013-07-30 44 views
0

考慮下面的代碼:__LINE__拉姆達inconsistenly評估則傳遞給函數宏

1. #include <iostream> 
2. #define CALL_FUNC(f) f(); 
3. 
4. int main() 
5. { 
6. CALL_FUNC([](){ 
7.  std::cout << "I'm on line " << __LINE__ << std::endl; 
8. }); 
9. return 0; 
10. } 

當我編譯這在Visual Studio 2012和g ++ 4.7.3,我得到的輸出「我在線8「。

當我在clang中編譯這個時,我得到了輸出結果「我在第7行」,這是我的預期。

有誰知道這些是正確的行爲?有沒有辦法在VS和g ++中獲得所需的行爲,同時還有一個CALL_FUNC宏?

+0

ideone(克++ 4.7.2示出了線9 ...的http:// ideone

這可以如下來證明.com/afoTTj;但這可能只是一些編輯器不匹配 – Asaf

+1

@Asaf你在'#include'和'#define之間添加了一個換行符 – Rapptz

+0

我只是想看看你是否注意了...是... – Asaf

回答

2

簡單的測試似乎表明,如果你移動});了線它的工作原理:

std::cout << "I'm on line " << __LINE__ << std::endl;}); 

GCC和MSVC認爲它是因爲});下一行的下一行。 (另外,你有一個額外的分號BTW)。

+0

但爲什麼不一致發生? – iolo

+7

因爲GCC(和我假定MSVC)只跟蹤語句和完整表達式的行號,而不是子表達式,宏的末尾在第8行。Clang跟蹤子表達式 –

+0

我認爲這種方法可行,因爲__LINE__位於宏調用結束的位置。在lambda中有幾行包含__LINE__語句將使它們全部評估爲調用的最後一行。 –

3

該標準未指定宏擴展和__LINE__預定義宏之間的交互。特別是跨越多行且包含__LINE__令牌的宏調用在不同的預處理器上表現出不同的行爲。當擴展宏參數的一部分時,一些會給它包含宏頭名稱(6)的行號,一些右括號(8)和一些__LINE__令牌行(7)。它取決於所使用的算法,並且有幾個是有效的和符合標準的。

#define F(x) x 
F(
__LINE__ 
) 

一些預處理器將輸出2,約3,部分4

+0

我會超越你所說的一步:我認爲標準甚至允許(通過不要禁止它),在'#define LINE __LINE__'之後,對於'LINE'總是擴展到相同的數字,不管它出現在哪裏,聲稱當LINE被展開時,當前源代碼行變成宏定義。 – hvd

+0

@hvd:不,標準規定宏在使用點展開,而不是定義點 - 因此很顯然'__LINE__'擴展到使用點處的假定行號。這是「推定行號」的概念,因爲它適用於多行宏調用以及出現變化的位置,因此不清楚。 –

+0

標準說'__LINE__'展開爲「當前源代碼行*(在當前源代碼文件中)*」(強調我的),但是根本沒有指定*哪個源代碼行計爲「當前」。你說得對,'__LINE__'在我的例子中沒有在宏定義的時候被擴展,但我沒有聲稱它是這樣做的。一個實現可以說:「好的,現在在第5行,我看到'LINE',我將擴展它,定義在第1行,所以當前源代碼行是第1行,直到擴展完成。 – hvd