[英]What determines whether a constexpr function is a constant expression?
(據我所知,使用的編譯器是 gcc 和 c++17 (在 Visual Studio 中很難找到這個))
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
increment( v );
return v;
}
int main( )
{
cout << f( ) << '\n';
}
上面的代碼給出了編譯錯誤:
constexpr function 'f' 不能產生常量表達式。
據我了解,這是因為 function increment
不是 constexpr。 讓我感到困惑的是,以下代碼編譯得很好:
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
for( int i = 0; i < 1; ++i )
{
increment( v );
}
return v;
}
int main( )
{
cout << f( ) << '\n';
}
這段代碼在功能上是相同的,並且可以編譯,即使增量仍然不是 constexpr。 我不明白通過范圍 [0, 1) 的 for 循環如何導致編譯器意識到 function f
實際上是 constexpr。
如果有人可以對 c++ 中的 constexpr 和這種明顯的不一致提供一些見解,我將不勝感激。
根據[dcl.constexpr]/6 ,這兩個程序都是“格式錯誤,不需要診斷”:
對於既不是默認值也不是模板的 constexpr function 或 constexpr 構造函數,如果不存在參數值,則 function 或構造函數的調用可以是核心常量表達式的求值子表達式,或者,對於構造函數,求值子表達式一些常量初始化的 object ( [basic.start.static] ) 的初始化完整表達式,程序格式錯誤,不需要診斷。
有點奇怪,gcc 只是沒有注意到第二個程序的問題,但它仍然符合要求。
請注意,如果在實際需要常量表達式的上下文中使用f
,則需要進行診斷,例如constexpr int n = f();
.
有些事情在 constexpr function 中是不允許的。 這些確實需要診斷(通常是錯誤消息),即使 function 從未在常量表達式中使用 - 請參閱cigien 的答案。 但是問題中的程序不違反任何這些更嚴格的規則。
由於您沒有在常量表達式中調用f
,因此您的問題是詢問編譯器是否需要僅根據其定義來診斷無法在常量表達式中調用f
。
這里列舉了對constexpr
function定義的要求:
constexpr function 的定義應滿足以下要求:
(3.1) 其返回類型(如果有)應為文字類型;
(3.2) 其每個參數類型應為字面量類型;
(3.3) 不能是協程;
(3.4) 如果 function 是構造函數或析構函數,其 class 不得有任何虛擬基類;
(3.5) 其函數體不得包含
(3.5.1) goto 語句,
(3.5.2) 標識符 label,
(3.5.3) 非文字類型或 static 或線程存儲持續時間的變量的定義。
可以看出, f
的定義沒有違反列表中的任何要求。 因此,如果編譯器選擇不診斷這一點,它就是符合標准的。
正如aschepler 的回答中指出的那樣,像f
這樣的constexpr
函數不能在常量表達式中調用,但不能被診斷,被認為是格式錯誤的,不需要診斷。
您實際上並沒有在編譯時“調用” f
。
如果您的主要 function 包括: static_assert(f() == 1, "f() returned 1");
我懷疑您會收到“f() 不是常量表達式”錯誤。
這是一個相關的問題
該標准要求constexpr
function 實際上可以在編譯時對某些參數集進行評估,但不是全部。 它不需要編譯器來診斷constexpr
function 在某些情況下執行某些可能是非編譯時的事情,甚至不需要編譯器來診斷這樣的 function 是否具有這樣的一組參數。 這避免了他們不得不解決停機問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.