2017-05-31 29 views
0

以下代碼在由g ++ -std = C++ 11編譯器編譯時失敗。在C++中使用具有不同數量參數的嵌套宏

# include<iostream> 
    # include<vector> 

    using namespace std; 


    # define stlf(x)  x.begin(), x.end() 
    # define repf(it, a, b) for(auto it = a ; it != b ; ++it) 


    /* 
    // Also, following alternative fails 

    # define repf(it, a, b) for(auto it = a ; it != b ; ++it) 
    # define stlf(x)  x.begin(), x.end() 

    */ 



    typedef vector<int > vi; 

    # define pd(x) printf("%d", x); 

    int main(void){ 

     vi arr(10, -1); 

     repf(arr, stlf(arr)) 
      pd(arr[i]); 


     return 0; 
    } 

這究竟是爲什麼?

2.可能已經實現問題的C++預處理器實現者,他們避免了這個功能呢?

3.我該如何使用這種快捷方式?

+0

你應該避免這種「快捷方式」,它會讓別人難以閱讀你的代碼 –

回答

4

你的兩個備選方案是相同的。宏定義的順序與其擴展無關;它只與擴展時定義的相關。

這是爲什麼發生?

您使用兩個參數調用宏repf,但它需要三個參數。這是一個錯誤,很簡單,所以預處理失敗。

C++預處理器實現者可能會遇到什麼實現問題,他們避免了這種特性?

我想你在這裏做了一個毫無根據的假設。問題不在於預處理器「缺少」某些「功能」;這是你對預處理器工作原理的期望是錯誤的。

想必,你期望的預處理器做這樣的事情:

  1. repf(arr, stlf(arr))
  2. repf(arr, arr.begin(), arr.end())
  3. for(auto it = arr.begin() ; it != arr.end() ; ++it)

...其中步驟1到步驟2,stlf(arr)得到擴大;然後將其擴展放入repf的調用中,然後在步驟3進行擴展。

問題是,這不是預處理器的工作原理。給出的例子是壞了,我無法說明的步驟正確地與此,讓我們假設我們這樣做不是爲了說明:

#define FOO(X, Y) BAR(X, Y) 
#define BAR(X,Y,Z) x is X y is Y z is Z 
#define ACOMMAB A, B 
FOO(ACOMMAB, C) 

最後一行擴展到x is A y is B c is Z,和它的作品更是這樣的:

  1. FOO(ACOMMAB, C)
  2. BAR(ACOMMAB, C)
  3. BAR(A, B, C)
  4. x is A y is B c is Z

請注意,內部宏不首先擴展;相反,外宏一樣。還要注意,這個例子注入一個逗號;所以注射逗號是肯定的東西,你可以做實際上,我認爲是「功能」你指的是被避免。

怎樣才能再使用這種快捷方式?

由於預處理器不工作,你覺得它的方式,你可能不會想用它做什麼,你認爲你想用它做......即使是速度編碼。 stlf會很高興地建立兩個參數爲你的函數調用,但宏不是函數。遠射似乎是你最好的選擇。

+0

很好的解釋!那麼,如果執行順序是Inner to Outer,那麼這個功能可能是可能的嗎?並有任何方法來驗證向外擴展? –

+1

@premktiw「以任何方式驗證外向內擴展」不要按照字面上的描述順序執行;這個「訂單」只是一個描述行爲的設備。這裏的關鍵是擴展的參數適用於替換列表,而不是調用,這是由我給出的例子的作品,但你的原始不(這在g ++中可以調用預處理器使用'-E'標誌,'-P'標誌會清除這樣的輸出)。此行爲由標準指定。 –

+0

...「訂單」設備還有助於查看異常情況。例如,是否擴大論點取決於替換列表;字符串化的參數或被粘貼的參數不會被展開。實現也可能放棄擴展一個沒有出現在替換列表中的爭論。但嚴格來說,在將其擴展到替換列表之前,可能會發生擴展論點。 –

0

應該是這樣的:

define repf(it, a) for(auto it = std::begin(a) ; it != std::end(a) ; ++it) 

但是你爲什麼要使用宏是爲了這個?

+0

不是這樣的,所以爲了使用repf(it,a,b)也可以用於一般循環如:int i; repf(i,0,10): - 會爲我重複10次! :) –

1

repf正在等待3個參數,而您傳遞的只是2.您不能將另一個宏的擴展結果(stlf)用作參數(對於另一個宏)。預處理器/編譯器不是以這種方式設計的。

我不反對宏(它們在很多情況下非常有用),但對於這種簡單的情況,你不能使用宏。它使你的程序很難讀取,維護和調試。避免!

理想的情況下,一系列基於for循環,你應該使用,而不是(沒有宏):

for(int a : arr) ... 
+0

那麼我可以使用一個宏的擴展作爲參數!我的主要問題是問題2。同樣在競爭性編程中,這些快捷鍵可以節省大量時間:)所以我真的需要它來工作。例如作爲參數的宏擴展:sort(stlf(arr)); –

1

的問題是,以H沃爾特斯指出,爲了其中宏做的事情

  • 讀取(解析)參數從輸入令牌流
  • 擴大在這些參數的宏(除非他們涉及#或##運算符)
  • 將那些(擴展的)參數替換到宏的主體中
  • 重新掃描正文以使其他宏能夠展開。

請注意,上面有兩個位置可以擴展其他宏,所以雖然順序可能對於您想要執行的操作是錯誤的,但您可以通過添加額外的宏調用以使其正確當你想要的時候讓事情發生。舉例來說,如果你有一個宏觀

#define expand(...) __VA_ARGS__ 

它什麼也不做(只是需要一個或多個參數,並在其中擴展宏),你可以用它來獲取正確的地方附加宏展開:

expand(repf expand((arr, stlf(arr)))) 

將擴大到你想要的for(auto it = arr.begin() ; it != arr.end() ; ++it)

這看似笨拙,但你可以用呼叫在另一個recrusive宏展開:

# define stlf(x)  x.begin(), x.end() 
# define expand(...) __VA_ARGS__ 
# define repf(...) expand(repf2 expand((__VA_ARGS__))) 
# define repf2(it, a, b) for(auto it = a ; it != b ; ++it) 

現在repf(arr, stlf(arr))擴大你想要的方式。