1. ホーム
  2. c++

[解決済み] C言語の文字列の長さをコンパイル時に計算する。これは本当にconstexprなのか?

2023-01-17 14:43:34

質問

コンパイル時に文字列リテラルの長さを計算しようとしています。これを行うために、私は次のコードを使用しています。

#include <cstdio>

int constexpr length(const char* str)
{
    return *str ? 1 + length(str + 1) : 0;
}

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}

すべてが期待通りに動作し、プログラムは4と8を表示します。clangによって生成されたアセンブリコードは、結果がコンパイル時に計算されることを示しています。

0x100000f5e:  leaq   0x35(%rip), %rdi          ; "%d %d"
0x100000f65:  movl   $0x4, %esi
0x100000f6a:  movl   $0x8, %edx
0x100000f6f:  xorl   %eax, %eax
0x100000f71:  callq  0x100000f7a               ; symbol stub for: printf

質問: この規格で保証されているのは length 関数がコンパイル時に評価されることは保証されていますか?

もしこれが本当なら、コンパイル時に文字列リテラルを計算するための扉が開かれたことになります。

どのように解決するのですか?

定数式はコンパイル時に評価されることが保証されていません。私たちは、非正規の引用である C++ 標準のドラフト セクション 5.19 定数表現 というのは、こう書いてありますが。

[...]>[ 注意: 定数式は翻訳中に評価されることがあります。 注:定数式は翻訳中に評価されることがあります。]

結果を代入して constexpr 変数に代入することで、コンパイル時に評価されることを確認できます。 Bjarne Stroustrup の C++11 リファレンス というものです( 強調 ):

コンパイル時に式を評価できるだけでなく ができるようにしたいです。 コンパイル時に評価されるように式を要求したい。 変数定義の前にあるconstexprがそれを行います。 (そして constを意味する)。

例えば

constexpr int len1 = length("abcd") ;

Bjarne Stroustrupは、コンパイル時の評価を保証できる場合について、次のようにまとめています。 isocppブログエントリ で、こう言っています。

[...]正しい答えは、Herbが述べたように は、標準によると、constexpr関数は、コンパイラ時または実行時に評価されるかもしれません。 定数式として使用されない限り、コンパイラ時または実行時に評価されます。 定数式として使われる場合は、コンパイル時に評価されなければならない。 コンパイル時に評価されなければならない。コンパイル時の評価を保証するためには、定数式として使うか を定数式として使うか(例えば,配列の束縛や大文字小文字の区別など),あるいは として)使うか,constexprを初期化するために使わなければなりません。私は 自尊心のあるコンパイラはこの最適化の機会を見逃すことはないでしょう。 私が最初に言ったように、コンパイラが最適化の機会を逃さないことを望みます。 は、その引数がすべて定数であれば、コンパイル時に評価されます。 式である場合、コンパイル時に評価されます。

つまり、これはコンパイル時に評価されるべき2つのケースの概要を示しています。

  1. 定数式が必要な場合に使用する。これは、標準規格の草稿の中で shall be ... converted constant expression または shall be ... constant expression が使用されている場合、配列バウンドなど。
  2. を初期化するために使用します。 constexpr を初期化するために使用します。