![](/img/trans.png)
[英]Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class
[英]constexpr initializing static member using static function
我想要從constexpr
函數計算的constexpr
值(即編譯時常量)。 而且我希望這兩個范圍都在類的命名空間內,即類的靜態方法和靜態成員。
我首先用(對我來說)顯而易見的方式寫了這個:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x
說:
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x
抱怨:
error: field initializer is not constant
好吧,我想,也許我必須把東西移出類體。 所以我嘗試了以下方法:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3
將毫無怨言地編譯它。 不幸的是,我的其他代碼使用了一些基於范圍的for
循環,所以我必須至少有 4.6。 現在我仔細查看了支持列表,似乎constexpr
也需要 4.6。 使用g++-4.6.3
我得到
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
這對我來說聽起來很奇怪。 這里的東西“在constexpr
有何不同”? 我不想添加-fpermissive
因為我更喜歡嚴格檢查我的其他代碼。 將foo
實現移到類體之外沒有明顯的效果。
有人可以解釋這里發生了什么嗎? 我怎樣才能實現我正在嘗試做的事情? 我主要對以下類型的答案感興趣:
其他有用的答案也是受歡迎的,但可能不會那么容易被接受。
該標准要求(第 9.4.2 節):
可以在類定義中使用
constexpr
說明符聲明文字類型的static
數據成員; 如果是這樣,它的聲明應指定一個大括號或等號初始化器,其中每個作為賦值表達式的初始化器子句都是一個常量表達式。
在您的“第二次嘗試”和 Ilya 的答案中的代碼中,聲明沒有大括號或相等的初始化程序。
你的第一個代碼是正確的。
不幸的是 gcc 4.6 不接受它,而且我不知道在哪里可以方便地嘗試 4.7.x(例如 ideone.com 仍然停留在 gcc 4.5 上)。
這是不可能的,因為不幸的是,標准禁止在類完成的任何上下文中初始化靜態constexpr
數據成員。 9.2p2 中大括號或相等初始化器的特殊規則僅適用於非靜態數據成員,但這是靜態的。
最可能的原因是constexpr
變量必須作為編譯時常量表達式從成員函數體內部可用,因此變量初始值設定項在函數體之前完全定義——這意味着函數仍然不完整(未定義) ) 在初始值設定項的上下文中,然后此規則生效,使表達式不是常量表達式:
未定義的調用
constexpr
功能或未定義constexpr
a的定義之外構造constexpr
函數或constexpr
構造;
考慮:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1) Ilya 的示例應該是無效代碼,因為靜態 constexpr 數據成員 bar被初始化為違反標准中的以下語句:
9.4.2 [class.static.data] p3: ... 可以在類定義中使用 constexpr 說明符聲明文字類型的靜態數據成員; 如果是這樣,它的聲明應指定一個大括號或等號初始化器,其中每個作為賦值表達式的初始化器子句都是一個常量表達式。
2)MvG問題中的代碼:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
就我所見和直覺上來說是有效的,因為靜態成員 foo(int)是由bar開始的時間處理定義的(假設自上而下處理)。 一些事實:
在 constexpr 函數或 constexpr 構造函數的定義之外調用未定義的 constexpr 函數或未定義的 constexpr 構造函數;
class C1 { constexpr static int foo() { return bar; } constexpr static int bar = foo(); };
看起來無效,但出於不同的原因,而不僅僅是因為foo在bar的初始值設定項中被調用。 邏輯如下:
但通過在子彈9(5.19 P2),該桿不滿足,因為它尚未初始化:
- 左值到右值的轉換 (4.1) 除非它應用於:
- 整數或枚舉類型的泛左值,它引用具有前面初始化的非易失性 const 對象,用常量表達式初始化。
因此bar的左值到右值轉換不會產生不符合 (9.4.2 p3) 要求的常量表達式。
調用帶有參數的 constexpr 函數,當被函數調用替換 (7.1.5) 替換時,不會產生常量表達式
#include <iostream>
class C1
{
public:
constexpr static int foo(constexpr int x)
{
return x + 1;
}
static constexpr int bar;
};
constexpr int C1::bar = C1::foo(sizeof(int));
int main()
{
std::cout << C1::bar << std::endl;
return 0;
}
這種初始化效果很好,但只適用於 clang
可能,這里的問題與類中聲明/定義的順序有關。 眾所周知,您甚至可以在類中聲明/定義任何成員之前使用它。
當您在類中定義 de constexpr 值時,編譯器沒有可用的 constexpr 函數,因為它在類內部。
也許,與此想法相關的Philip回答是理解問題的好點。
請注意此代碼編譯沒有問題:
constexpr int fooext(int x) { return x + 1; }
struct C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = fooext(5);
};
constexpr static int barext = C1::foo(5);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.