クラスの非静的メンバーの配列の要素数を定数式として取得したい


クラスの非静的メンバーの配列の要素数を非静的メンバー関数内で定数式として取得したいのですが、`std::size`関数を使用するとコンパイルエラーになってしまいます。  
このような状況では`std::size`関数を使用は適さないのでしょうか？  
`sizeof(buffer)/sizeof(buffer[0])`とすれば定数式で取得できますができれば避けたいと思っています。

コード:

[code]

    #include <stdio.h>
    #include <iterator>
    
    struct S {
        int buffer[2048];
    
        void foo(){
            constexpr size_t N = std::size(buffer);
            //constexpr size_t N = sizeof(buffer)/sizeof(buffer[0]);
            // Nを使った処理...
            printf("%zu\n", N);
        }
    };
    
    int main(void)
    {
        S s;
        s.foo();
    }
    
[/code]

clang 15での実行結果:

[code]

    $ clang++ prog.cc -Wall -Wextra -pedantic -std=c++20 -lm
    prog.cc:8:26: error: constexpr variable 'N' must be initialized by a constant expression
            constexpr size_t N = std::size(buffer);
                             ^   ~~~~~~~~~~~~~~~~~
    prog.cc:8:40: note: implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function
            constexpr size_t N = std::size(buffer);
                                           ^
    1 error generated.
    
[/code]

  * gcc 12でも同様のコンパイルエラーになります。
  * 定数式でなければ(`const`ならば)、`std::size`関数はコンパイルエラーにはなりません。




エラーメッセージにも出ている通り、暗黙に `this` を経由する形になるのが定数式の定義から外れてしまうので (実際には `this`
の情報を使っていなくても) エラーという扱いになってしまいます。

`sizeof` や `decltype`
のオペランドなど「式が評価されない」と定義されている場合があり、そういうところには評価した結果が未定義になってしまうような式であっても書けますし、
`this` を含んでいてもコンパイル時に解決されるのでそのような性質を活用するとよいでしょう。

`type_traits` にある `std::extend`
は配列型から配列の大きさを抽出するメタ関数なのでこれを用いれば簡単に配列の大きさを知ることが出来ます。

[code]

    #include <cstdio>
    #include <type_traits>
    
    struct S {
        int buffer[2048];
    
        void foo() const {
            constexpr size_t N = std::extent_v<decltype(buffer)>;
            std::printf("%zu\n", N);
        }
    };
    
    int main(void)
    {
        S s;
        s.foo();
    }
    
[/code]




エラーメッセージにも出ている通り、暗黙に `this` を経由する形になるのが定数式の定義から外れてしまうので (実際には `this`
の情報を使っていなくても) エラーという扱いになってしまいます。

`sizeof` や `decltype`
のオペランドなど「式が評価されない」と定義されている場合があり、そういうところには評価した結果が未定義になってしまうような式であっても書けますし、
`this` を含んでいてもコンパイル時に解決されるのでそのような性質を活用するとよいでしょう。

`type_traits` にある `std::extend`
は配列型から配列の大きさを抽出するメタ関数なのでこれを用いれば簡単に配列の大きさを知ることが出来ます。

[code]

    #include <cstdio>
    #include <type_traits>
    
    struct S {
        int buffer[2048];
    
        void foo() const {
            constexpr size_t N = std::extent_v<decltype(buffer)>;
            std::printf("%zu\n", N);
        }
    };
    
    int main(void)
    {
        S s;
        s.foo();
    }
    
[/code]

