[英]removing constexpr from a variable capturing a constexpr function return value removes compile-time evaluation
考慮以下constexpr
函數static_strcmp
,它使用C ++ 17的constexpr
char_traits::compare
函數:
#include <string>
constexpr bool static_strcmp(char const *a, char const *b)
{
return std::char_traits<char>::compare(a, b,
std::char_traits<char>::length(a)) == 0;
}
int main()
{
constexpr const char *a = "abcdefghijklmnopqrstuvwxyz";
constexpr const char *b = "abc";
constexpr bool result = static_strcmp(a, b);
return result;
}
godbolt顯示這在編譯時得到評估,並優化到:
main: xor eax, eax ret
從bool result
刪除constexpr
:
如果我們從constexpr bool result
刪除constexpr
,現在調用不再優化。
#include <string>
constexpr bool static_strcmp(char const *a, char const *b)
{
return std::char_traits<char>::compare(a, b,
std::char_traits<char>::length(a)) == 0;
}
int main()
{
constexpr const char *a = "abcdefghijklmnopqrstuvwxyz";
constexpr const char *b = "abc";
bool result = static_strcmp(a, b); // <-- note no constexpr
return result;
}
godbolt顯示我們現在調用memcmp
:
.LC0: .string "abc" .LC1: .string "abcdefghijklmnopqrstuvwxyz" main: sub rsp, 8 mov edx, 26 mov esi, OFFSET FLAT:.LC0 mov edi, OFFSET FLAT:.LC1 call memcmp test eax, eax sete al add rsp, 8 movzx eax, al ret
添加短路length
檢查:
如果我們在調用char_traits::compare
之前首先比較static_strcmp
兩個參數的char_traits::length
, 而不在bool result
上使用 constexpr
,則再次優化調用。
#include <string>
constexpr bool static_strcmp(char const *a, char const *b)
{
return
std::char_traits<char>::length(a) == std::char_traits<char>::length(b)
&& std::char_traits<char>::compare(a, b,
std::char_traits<char>::length(a)) == 0;
}
int main()
{
constexpr const char *a = "abcdefghijklmnopqrstuvwxyz";
constexpr const char *b = "abc";
bool result = static_strcmp(a, b); // <-- note still no constexpr!
return result;
}
godbolt顯示我們回到了被優化的電話:
main: xor eax, eax ret
static_strcmp
中刪除constexpr
導致常量評估失敗? constexpr
,在編譯時也會對char_traits::length
的調用進行求值,那么為什么在static_strcmp
的第一個版本中沒有constexpr
出現相同的行為? 我們有三個工作案例:
1)初始化constexpr
值或嚴格要求編譯時已知值(非類型模板參數,C樣式數組的大小, static_assert()
的測試,...時需要計算值... )
2) constexpr
函數使用的值不是編譯時知道的(例如:從標准輸入接收的值)。
3) constexpr
函數接收值編譯時已知但結果是在一個不需要編譯時的地方。
如果我們忽略了as-if規則,我們就有:
在情況(1)中,編譯器必須計算值編譯時,因為計算值是編譯時所需的
在情況(2)中,編譯器必須計算值運行時,因為它不可能計算它的編譯時間
在情況(3)中,我們處於灰色區域,編譯器可以在其中計算編譯時的值,但計算值不是嚴格要求編譯時; 在這種情況下,編譯器可以選擇計算編譯時還是運行時。
用初始代碼
constexpr bool result = static_strcmp(a, b);
你遇到的情況是(1):編譯器必須計算編譯時間,因為result
變量被聲明為constexpr
。
刪除constexpr
,
bool result = static_strcmp(a, b); // no more constexpr
你的代碼在灰色區域(case(3))中進行轉換,其中編譯時計算是可能的但不是嚴格要求的,因為輸入值是已知的編譯時間( a
和b
),但結果是在值不是編譯的地方 - 所需時間(普通變量)。 因此編譯器可以選擇,在您的情況下,選擇使用該函數版本的運行時計算,使用另一個版本進行編譯時計算。
注意,標准中沒有任何內容明確要求在編譯時調用constexpr
函數,請參見最新草案中的9.1.5.7:
對constexpr函數的調用產生與在所有方面調用等效的非constexpr函數相同的結果,除了(7.1)對constexpr函數的調用可以出現在常量表達式中並且(7.2)不執行復制省略常量表達式([class.copy.elision])。
(強調我的)
現在,當調用出現在常量表達式中時,編譯器無法避免在編譯時運行該函數,因此它盡職盡責。 當它沒有時(如在你的第二個片段中),它只是缺少優化的情況。 這里的人並不缺。
您的程序具有未定義的行為,因為您總是比較strlen(a)
字符。 字符串b
沒有那么多字符。
如果您將字符串修改為相等長度(因此您的程序定義明確),您的程序將按預期進行優化 。
所以這不是錯過優化。 編譯器會優化您的程序,但由於它包含未定義的行為,因此不會對其進行優化。
注意,無論是否是未定義的行為,都不是很清楚。 考慮到編譯器使用memcmp
,它認為兩個輸入字符串必須至少為strlen(a)
long。 所以根據編譯器的行為,它是未定義的行為。
以下是目前的標准草案中有關比較的內容:
返回 :如果對於[0,n)中的每個i,則返回 0,X :: eq(p [i],q [i])為
true
; 否則,如果對於[0,n)中的某些j,X :: lt(p [j],q [j])為true
並且對於[0,j)X :: eq(p)中的每個i,則為負值[i],q [i])是true
; 否則是正值。
現在,沒有指定是否允許compare
讀取p[j+1..n)
或q[j+1..n)
(其中j
是第一個差異的索引)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.