簡體   English   中英

constexpr 使用靜態函數初始化靜態成員

[英]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實現移到類體之外沒有明顯的效果。

預期答案

有人可以解釋這里發生了什么嗎? 我怎樣才能實現我正在嘗試做的事情? 我主要對以下類型的答案感興趣:

  • 一種在 gcc-4.6 中完成這項工作的方法
  • 觀察到后來的 gcc 版本可以正確處理其中一個版本
  • 一個指向規范的指針,根據該規范,我的至少一個構造應該可以工作,以便我可以讓 gcc 開發人員真正讓它工作
  • 根據規范,我想要的信息是不可能的,最好是對此限制背后的基本原理有所了解

其他有用的答案也是受歡迎的,但可能不會那么容易被接受。

該標准要求(第 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開始的時間處理定義的(假設自上而下處理)。 一些事實:

  • 我確實同意,盡管在調用 foo 時類 C1並不完整(基於 9.2p2),但是類 C1 的完整性或不完整性並沒有說明就標准而言是否定義了 foo。
  • 我確實搜索了成員函數定義的標准,但沒有找到任何東西。
  • 因此,如果我的邏輯有效,Ben 提到的陳述不適用於這里:

    在 constexpr 函數或 constexpr 構造函數的定義之外調用未定義的 constexpr 函數或未定義的 constexpr 構造函數;

3)Ben給出的最后一個例子,簡化了:

 class C1 { constexpr static int foo() { return bar; } constexpr static int bar = foo(); };

看起來無效,但出於不同的原因,而不僅僅是因為foobar的初始值設定項中被調用。 邏輯如下:

  • foo()靜態 constexpr 成員 bar的初始化程序中被調用,因此它必須是一個常量表達式(9.4.2 p3)。
  • 因為它是對 constexpr 函數的調用,所以函數調用替換(7.1.5 p5) 開始了。
  • 它們沒有函數的參數,所以剩下的是“將結果返回的表達式或花括號初始化列表隱式轉換為函數的返回類型,就像通過復制初始化一樣。” (7.1.5 p5)
  • 返回表達式只是bar ,它是一個左值,需要進行左值到右值的轉換。
  • 但通過在子彈9(5.19 P2),該不滿足,因為它尚未初始化:

    • 左值到右值的轉換 (4.1) 除非它應用於:
      • 整數或枚舉類型的泛左值,它引用具有前面初始化的非易失性 const 對象,用常量表達式初始化。
  • 因此bar的左值到右值轉換不會產生不符合 (9.4.2 p3) 要求的常量表達式。

  • 因此,根據 (5.19 p2) 中的第 4 項,對foo()的調用不是常量表達式:

    調用帶有參數的 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.

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