93445


クラスの非静的メンバーの配列の要素数を非静的メンバー関数内で定数式として取得したいのですが、`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`関数はコンパイルエラーにはなりません。


CC BY-SA 4.0
2023-01-18T09:30:57.787
93436
2023-02-24T05:37:18.517
9113
question
c++
クラスの非静的メンバーの配列の要素数を定数式として取得したい


あまりいい回答ではありませんが、C配列を `std::array` に変更し、`std::size` の代わりに `std::tuple_size_v`
（もしくは`std::tuple_size`）を使えば取れました。

[code]

    struct S {
        std::array<int, 2048> buffer;
    
        void foo() {
            constexpr size_t N = std::tuple_size_v<decltype(buffer)>;
            // Nを使った処理...
            printf("%zu\n", N);
        }
    };
    
[/code]


CC BY-SA 4.0
2023-01-18T22:25:31.367
93443
2023-01-18T22:25:31.367
4236
93436
answer


エラーメッセージにも出ている通り、暗黙に `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]


CC BY-SA 4.0
2023-01-19T02:31:38.733
93445
2023-01-19T02:31:38.733
3364
93436
answer


（この回答は質問に直接的に答えるものではなく、将来的な情報提供となります。）

暗黙の`this`ポインタ経由でアクセスされる非静的メンバー変数は、C++言語仕様の修正によって将来的にはコア定数式(core constant
expression)扱いになるはずです。

本件は [P2280R4 Using unknown pointers and references in constant
expressions](https://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2022/p2280r4.html) で議論された経緯があり、次期
[標準C++23にて欠陥報告(Defect
Report)扱い](https://github.com/cplusplus/papers/issues/973)
となることが決まりました。つまりP2280R4の内容はC++11まで遡及して修正適用されます。

残念ながら2023年2月現在は
[P2280R4対応済みC++コンパイラは存在しません](https://en.cppreference.com/w/cpp/compiler_support#C.2B.2B23_features)
が、いずれはどのC++コンパイラでも質問文中ソースコードでOKとなるはずです。

[code]

    struct S {
        int buffer[2048];
    
        void foo(){
            constexpr size_t N = std::size(buffer);
            // Nを使った処理...
        }
    };
    
[/code]


CC BY-SA 4.0
2023-02-24T05:37:18.517
93972
2023-02-24T05:37:18.517
49
93436
answer