2017-05-31 85 views
34

逗號我發現下面的有趣的代碼今天:混亂與三元表達

SomeFunction(some_bool_variable ? 12.f, 50.f : 50.f, 12.f) 

我創建一個小樣本重現行爲:

class Vector3f 
{ 
public: 
    Vector3f(float val) 
    { 
     std::cout << "vector constructor: " << val << '\n'; 
    } 
}; 

void SetSize(Vector3f v) 
{ 
    std::cout << "SetSize single param\n"; 
} 

void SetSize(float w, float h, float d=0) 
{ 
    std::cout << "SetSize multi param: " << w << ", " << h << ", " << d << '\n'; 
} 

int main() 
{ 
    SetSize(true ? 12.f, 50.f : 50.f, 12.f); 
    SetSize(false ? 12.f, 50.f : 50.f, 12.f); 
} 

Live Sample

結果我從運行上面得到的代碼是:

clang++ -std=c++14 -O2 -Wall -pedantic -lboost_system -lboost_filesystem -pthread main.cpp && ./a.out 
main.cpp:29:20: warning: expression result unused [-Wunused-value] 
    SetSize(true ? 12.f, 50.f : 50.f, 12.f); 
        ^~~~ 
main.cpp:30:21: warning: expression result unused [-Wunused-value] 
    SetSize(false ? 12.f, 50.f : 50.f, 12.f); 
        ^~~~ 
2 warnings generated. 
SetSize multi param: 50, 12, 0 
SetSize multi param: 50, 12, 0 

我期待的這兩個的情況是,一個參數將被傳遞給SetSize(float)。然而,我發現兩個參數非常混亂(特別是因爲三元的優先級高於逗號;所以我認爲在這種情況下,逗號並不是分隔函數的參數)。例如,如果使用true,則三元應該導致12.f, 50.f。在這個表達式中,值的逗號左側被丟棄/忽略不計,所以我期待最終的結果是:

SetSize(50.f); 

混亂的第二部分是,我們是否使用truefalse三元,相同的2個值被傳遞給函數。 true的情況應該是h=12, w=50我想......

我看到編譯器試圖警告我一些事情,但我不明白是怎麼回事。有人可以分解這種邏輯,並逐步解釋結果嗎?

+1

您不能使用三元運算符提供參數列表。它只選擇值。 – EJP

+6

@EJP:讓我們用回答部分來回答問題。謝謝。 –

+0

條件運算符的語言語法不允許在最後一部分使用逗號 - 運算符表達式(爲了避免歧義) –

回答

30

雖然三元運算符的第二部分是自包含的,但第三部分不是。語法如下:

條件表達式

邏輯或表達

邏輯或表達?表達式:賦值表達式

所以你的函數調用是有效的:

SetSize((true ? (12.f, 50.f): 50.f), 12.f) 

所以三元表達true ? (12.f, 50.f): 50.f被評爲第一個參數的函數。然後12.f作爲第二個值傳遞。這種情況下的逗號是而不是逗號運算符,但函數參數分隔符。

C++ standard的部分5.18:

在上下文其中逗號被賦予特殊的含義,[ 例:中的參數列表,以函數(5.2.2)和列表初始值設定項(8。5) - 結束示例]第5章中描述的逗號運算符 只能出現在括號內。 [ 示例:

f(a, (t=3, t+2), c); 

有三個參數,其中第二個具有值5。 - 年底 例如]

如果你想在最後兩個子表達式組合在一起,你需要添加括號:

SetSize(true ? 12.f, 50.f : (50.f, 12.f)); 
SetSize(false ? 12.f, 50.f : (50.f, 12.f)); 

現在你有一個逗號操作符和單參數版本SetSize被調用。

17

這是因爲C++ does not treat the second comma as a comma operator

在各種逗號分隔的列表,諸如函數的參數列表f(a, b, c)和初始化列表int a[] = {1,2,3}逗號,不是逗號運算符。

就第一個逗號而言,C++沒有別的選擇,只能把它當作逗號運算符。否則,解析將是無效的。

查看它是認爲只要C++解析器在上下文中發現?其中逗號分隔符是允許的,它會尋找匹配:完成表達式的第一部分,和然後的一個簡單的方法相匹配少是完成第二個表達式所必需的。即使刪除了雙重過載,第二個逗號也不會被視爲運算符。

10

編譯器警告你,你正在丟掉恰好50%的浮點文字。

讓我們來分解它。

// void SetSize(float w, float h, float d=0) 
SetSize(true ? 12.f, 50.f : 50.f, 12.f); 
//  ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ 

這裏,我們提出使用條件運算作爲第一個參數字面12.f作爲第二個參數的表達式,和;第三個參數保留爲其默認值(0)。

是的,真的。

它解析像這樣的(因爲沒有其他解析它有效途徑):

SetSize((true ? 12.f, 50.f : 50.f), 12.f); 
//  ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ 

第二個參數的值是直接的,那麼讓我們來看看第一:

true ? 12.f, 50.f : 50.f 

這意思是:

  • 如果爲true,則結果爲12.f, 50.f
  • 否則,結果是50.f

那麼,真的是永遠是真的,所以我們可以立即打折第二個選項。

和表達式12.f, 50.f利用逗號操作,其評估兩個操作數然後夾頭掉在第二,第一和結果,即50.f

因此,整個事情居然是:

SetSize(50.f, 12.f); 

如果這是不是有些神祕的和毫無意義的節目「謎」,這是一個非常愚蠢的一段代碼,與一個沒有受過教育的程序員希望以「解壓」的表達成更相當於:

SetSize(
    (true ? 12.f : 50.f), 
    (true ? 50.f : 12.f) 
); 

&hellip;這是仍然可怕的和無用的代碼,因爲真實仍然是真實的。

(顯然,這些值是在false代替寫入的情況不同,但相同的邏輯適用。)


真正情況下應爲h = 12,W = 50 I」 d think ...

是的。這就是你發佈的輸出說的。當你不擅自重新安排參數時,它更清楚,即它們是w = 50 h = 12。