簡體   English   中英

從捕獲constexpr函數返回值的變量中刪除constexpr會刪除編譯時評估

[英]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))中進行轉換,其中編譯時計算是可能的但不是嚴格要求的,因為輸入值是已知的編譯時間( ab ),但結果是在值不是編譯的地方 - 所需時間(普通變量)。 因此編譯器可以選擇,在您的情況下,選擇使用該函數版本的運行時計算,使用另一個版本進行編譯時計算。

注意,標准中沒有任何內容明確要求在編譯時調用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.

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