簡體   English   中英

“ variableName;” C ++語句是否始終是空操作?

[英]Will a “variableName;” C++ statement be a no-op at all times?

在C ++中,有時會定義一個變量,但不會使用。 這是一個示例-與COM_INTERFACE_ENTRY_FUNC_BLIND ATL宏一起使用的函數:

HRESULT WINAPI blindQuery( void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/ ) 
{
    DEBUG_LOG( __FUNCTION__ ); //DEBUG_LOG macro expands to an empty string in non-debug
    DEBUG_LOG( iid );
    iid; // <<<<<<<----silence compiler warning
    if( ppv == 0 ) {
        return E_POINTER;
    }
    *ppv = 0;
    return E_NOINTERFACE;
}

在上面的示例中, iid參數與DEBUG_LOG宏一起使用, DEBUG_LOG宏在非調試配置中擴展為空字符串。 因此,注釋掉或刪除簽名中的iid變量名稱不是一種選擇。 編譯非調試配置時,編譯器會生成C4100: 'iid' : unreferenced formal parameter警告,因此,為了使警告iid;保持靜默iid; 添加被認為是無操作的語句。

問題如下:如果我們有以下任何聲明:

 CSomeType variableName; //or
 CSomeType& variableName; //or
 CSomeType* variableName;

將在C ++代碼中執行以下語句:

variableName;

始終保持無操作狀態,而與CSomeType是什么CSomeType

是的,但是您可能還會收到另一個警告。

這樣做的標准方法是: (void)iid;


從技術上講,這仍然可以將iid加載到寄存器中,並且什么也不做。 當然,在編譯器方面這是非常愚蠢的(如果它確實刪除了編譯器,我懷疑有人會這樣做),但是如果要忽略的表達式涉及可觀察到的行為(例如,對IO函數的調用或閱讀和寫作volatile的變量。

這就提出了一個有趣的問題:我們可以接受一個表達式並完全忽略它嗎?

也就是說,我們現在擁有的是:

#define USE(x) (void)(x)

// use iid in an expression to get rid of warning, but have no observable effect
USE(iid); 

// hm, result of expression is gone but expression is still evaluated
USE(std::cout << "hmmm" << std::endl);

這接近解決方案:

 
 
 
  
  // sizeof doesn't evaluate the expression #define USE(x) (void)(sizeof(x))
 
  

但是失敗:

 
 
 
  
  void foo(); // oops, cannot take sizeof void USE(foo());
 
  

解決方案是簡單地:

 
 
 
  
  // use expression as sub-expression, // then make type of full expression int, discard result #define USE(x) (void)(sizeof((x), 0))
 
  

這保證了沒有操作。

編輯:以上確實保證沒有效果,但是我沒有測試就發布了。 經過測試,它至少在MSVC 2010中再次生成警告,因為未使用該 不好,需要更多技巧!


提醒:我們想“使用”一個表達式而不對其求值。 如何才能做到這一點? 像這樣:

 #define USE(x) ((void)(true ? 0 : (x))) 

這有一個像上次一樣的簡單問題(實際上更糟),因為(x)需要轉換為int 再次,這很容易解決:

 #define USE(x) ((void)(true ? 0 : ((x), 0))) 

我們又回到了上次(沒有)的效果,但是這次x被“使用”了,所以我們沒有收到任何警告。 完成了吧?

此解決方案實際上仍然存在一個問題(並且在上一個未解決方案中也存在,但未被注意),在此示例中出現了:

 struct foo {}; void operator,(const foo&, int) {} foo f; USE(f); // oops, void isn't convertible to int! 

也就是說,如果表達式(x)的類型將逗號運算符重載為不可轉換為int ,則解決方案將失敗。 當然,不太可能,但是為了完全落伍,我們可以使用以下方法修復它:

 #define USE(x) ((void)(true ? 0 : ((x), void(), 0))) 

為確保我們最終得到零。 約翰內斯帶給你的這個把戲


還要注意的是,如果以上操作還不夠,那么足夠愚蠢的編譯器可能會將表達式0 (裝入寄存器或其他東西)“加載”,然后忽略它。

我認為這是不可能擺脫的,因為我們最終需要一個表達式來導致某種類型的類型被忽略,但是如果我想到了,我會添加它。

好吧,如果不查看編譯器的源代碼,實際上不可能100%地確定,但是如果在現代編譯器中生成任何代碼,我將感到非常驚訝。

歸根結底,如果您擔心任何特定的實例,那么您隨時可以查看生成的匯編代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM