簡體   English   中英

constexpr 函數中的靜態表

[英]Static table inside constexpr function

我有一段將整數映射到整數的生成代碼,其核心是一個簡單的表。 在 C++17 之前,它曾經是這樣的:

int convert (int v)
{
  static const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };

  if (0 <= v && v < sizeof table / sizeof table[0])
    return table[v];
  else
    return -1;
}

對於 C++17,我想使用 constexpr。 我希望將constexpr添加到函數簽名就足夠了,但我必須刪除表的static變量,這顯然沒有充分的理由使我的實現更加復雜。 更不用說非 constexpr 上下文中的table可能會在堆棧上,所以我想我應該用constexpr替換static

G++ 8 報告:

/tmp/foo.cc: In function 'constexpr int convert(int)':
/tmp/foo.cc:14:26: error: 'table' declared 'static' in 'constexpr' function
   static const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };
                          ^

和 Clang++ 7:

/tmp/foo.cc:14:20: error: static variable not permitted in a constexpr function
  static const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };
                   ^
1 error generated.

因為我希望這段代碼適用於所有 C++ 標准(並且在每種情況下都做正確的事情),所以我想我必須這樣寫(是的,宏,這不是問題所在):

#if 201703L <= __cplusplus
# define CONSTEXPR constexpr
# define STATIC_ASSERT static_assert
# define STATIC_OR_CONSTEXPR constexpr
#else
# include <cassert>
# define CONSTEXPR
# define STATIC_ASSERT assert
# define STATIC_OR_CONSTEXPR static
#endif

CONSTEXPR int convert (int v)
{
  STATIC_OR_CONSTEXPR const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };

  if (0 <= v && v < sizeof table / sizeof table[0])
    return table[v];
  else
    return -1;
}

int main()
{
  STATIC_ASSERT(convert(-42) == -1);
  STATIC_ASSERT(convert(2) == 6);
  STATIC_ASSERT(convert(7) == 8);
  STATIC_ASSERT(convert(8) == -1);
}

所以:

  • 是什么促使禁止在 constexpr 函數中使用靜態存儲變量?

  • 有沒有我可能錯過的更清潔的選擇? 當然,我可以將tableconvert中拉出來,但我想避免這種情況。

  • 標准是否保證非 constexpr 上下文中 constexpr 函數中的 const 數組將位於靜態存儲而不是堆棧中?

編輯:我之前的回答完全錯誤......所以我要解決這個問題!

我知道我已經很晚了,我相信你已經想出了我要說的一切。

不幸的是,您不能在 constexpr 函數中包含靜態變量。

C++14 標准規定 constexpr 函數體不能包含“變量的定義……靜態或線程存儲持續時間”。 這是有道理的,因為 constexpr 上下文是在這些運行時構造之外評估的。

但是我們使用的是 C++ 而不是 C,這意味着我們有對象。 對象可以有 constexpr 靜態存儲! 我的解決方案是將函數(作為靜態函數)包裝在一個對象/類型中,該對象/類型包含作為 constexpr 靜態成員的數據。

我知道靜態變量會導致非 constexpr 世界中的初始化問題,因此您可能需要使用預處理器根據您正在編譯的標准選擇一個工作版本。

這樣,C++14 代碼就變成了這樣:

class convert {
  static constexpr int table[] = {3, 2, 6, 1, 7, 1, 6, 8};
public:
  static constexpr int run(int v) {
    if (0 <= v && v < sizeof table / sizeof table[0])
      return table[v];
    else
      return -1;
  }
};

您可能還想添加一個static constexpr int/unsigned int/size_t來指定數組的長度,以刪除您必須做的丑陋的sizeof東西(盡管如果您針對多個標准,那么這可能不可行)。

您可以改為將其指定為模板參數(我相信)應該適用於所有標准!

至於你最后一個問題。 我很確定 constexpr 函數內的任何非靜態 constexpr 對象都將在非 constexpr 上下文中的堆棧上初始化(在 Compiler Explorer 上進行的一些測試似乎與此一致)。 主要區別在於,結果值將在“無”運行時成本的情況下立即構建(constexpr 存在的原因)。

我覺得這比其他答案更接近你原來的答案,模板有時會讓你頭疼(盡管我個人會使用可變參數模板方法來生成數組)

請參閱https://en.cppreference.com/w/cpp/language/constexpr函數內的靜態 constexpr 變量是否有意義? 了解更多信息!

  • 有沒有我可能錯過的更清潔的選擇? 當然,我可以將表從轉換中拉出來,但我想避免這種情況。

怎么樣(C ++ 11兼容):

template <int... Ns>
struct MyTable
{
    static constexpr int get(std::size_t v)
    {
        return v < sizeof...(Ns) ? data[v] : -1;
    }

    static constexpr int data[] = {Ns...};  
};

constexpr int convert (std::size_t v)
{
    using table = MyTable<3, 2, 6, 1, 7, 1, 6, 8>;

    return table::get(v);
}

演示

  • 標准是否保證非 constexpr 上下文中 constexpr 函數中的 const 數組將位於靜態存儲而不是堆棧中?

我不認為 constexpr 變量需要具體化,在 switch(或完美哈希)中轉換該代碼似乎是有效的實現。

所以我認為沒有保證。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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