
とあるC++プログラミングの入門書で、どうしても理解出来ないコードがあります。
配列の要素数の取得法で、通常は「sizeof (array) / sizeof *(array)」
*arrayは配列変数
としていたのを、テンプレートを使った別の方法として以下のようなコード
-----------------------------------------------------------
#include <iostream>
using namespace std;
#define ARRAY_SIZE(array) (sizeof *ARRAY_SIZE_(&(array)))
template <typename T,size_t N>
char (*ARRAY_SIZE_(T(*)[N]))[N];
int main() {
int n[40];
cout << ARRAY_SIZE(n) << endl;
}
-----------------------------------------------------------
&(array)がT型N要素の配列へのポインタ、それがtemplateから引数となって "T(*)[N]" になっているところまでは何となく理解できます。
*ARRAY_SIZE_がそれを括弧で括っているのは関数の様に見えますが、関数としての実装がありません。
"*ARRAY_SIZE_()"は関数なのでしょうか?
またchar型配列のポインタの実体はどこにあるのでしょうか?
結果、char型 "(*ARRAY_SIZE_(T(*)[N]))" の "[N]" 要素数の配列として返しているようですが、その前に既にsize_tの "N" として答えが出ているので助長にも見えます。
勿論、このコードのコンパイルは通って正しい答え「40」が得られました。
C++初心者で頓珍漢な質問かもしれませんが、よろしくご教示お願いします。
No.2ベストアンサー
- 回答日時:
文法的には
char (*ARRAY_SIZE_(T(*)[N]))[N];
は ARRAY_SIZE_ という関数の宣言で
・引数は T(*)[N] つまり「T 型の要素を N個持つ配列」へのポインタ
・返り値は char (*)[N] で「char 型の要素を N個持つ配列」へのポインタ
です. C の文法を受け継いでいるので非常に読みにくくなっていますが, 「ARRAY_SIZE_」の直後に「(」があることから「ARRAY_SIZE_ が関数名である」とわかりますし, 対応する「)」までが引数リストになります. そして, 引数リストまでの部分, つまり「ARRAY_SIZE_(T(*)[N])」の部分を「X」と置きなおすと
char (*X)[N];
となることから「関数を呼び出すと char (*)[N] が返ってくる」ことがわかります.
これも C++11 以降だと auto を使って
template <typename T,size_t N>
auto ARRAY_SIZE_(T(*)[N]) -> char (*)[N];
と書くことができるので, ちょっとは見やすいかと.
マクロ ARRAY_SIZE では返り値を * で逆参照して「char 型の要素を N個持つ配列」をつくり, それに対して sizeof を適用することで配列の要素数である N を求めています.
でもこれ, ポインタの代わりに参照を使って
#define ARRAY_SIZE(array) (sizeof ARRAY_SIZE_(array))
template <typename T,size_t N>
char (&ARRAY_SIZE_(T(&)[N]))[N];
としても同じなんじゃないかなぁ.
あとついでですが, ARRAY_SIZE_ を関数にしているのは「テンプレート引数を自動的に推定する」ためです. 関数テンプレートでは (可能なら) テンプレート引数を関数の実引数から推定します. つまり, 挙がっている例では
ARRAY_SIZE(n)
から (紆余曲折を経て)
T = int, N = 40
となります. しかし, この自動推定はクラステンプレートには働かないので,
template <typename T, size_t N, T (&)[N]>
struct ArraySize {
enum { size = N };
};
としても意味がありません. 逆に言うと「自動推定するためには関数テンプレートでなければならない」ので, 例えば
make_pair
のような補助関数があったりします.
Tacosanさん
とても詳しい解説ありがとうございます。
ロベールの…という有名なC++入門本の中の「コラム」に出てきたコードで、それまで理解していたのに、いきなり難しいコードが出てきたので立ち往生していました。
Tacosanさんの解説を何度も読み返して、理解できるまで頑張ります。
「解決」とさせていただきます。
どうもありがとうございました。
No.1
- 回答日時:
ARRAY_SIZE_ は関数ですけど, 今の場合その関数は sizeof のオペランドとしてしか使っていません. そして sizeof のオペランドは評価されないので, ARRAY_SIZE_ という関数そのものの実装は必要ありません (「char型配列の実体」も不必要). C++11 以降であれば constexpr を使って
template <typename T, size_t N>
constexpr size_t ARRAY_SIZE(T (&)[N]) { return N; }
とするところですが, C++98 (や C++03) に constexpr は存在しないので, 「コンパイル時定数にする」ために努力をしているものだと思われます.
あと, 「既にsize_tの "N" として答えが出ている」んだけど, その N はテンプレートの中にしか存在しないので, 外に持ち出すには関数の返り値であったり列挙型の要素として与えたりするなどの工夫が必要です.
Tacosanさん
回答ありがとうございます。
template内のsize_tの"N"を外へ持ち出す手段として、実装の無い関数"ARRAY_SIZE_(T(*)[N]))"を利用しているのですね。
理屈としては理解しました。
文法的なこともより理解できるようになりたいと思います。
どうもありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- PHP PHPでCSVを出力するさいに、ループの中で前の行の値を変更したい 3 2022/10/27 17:44
- PHP PHPでCSVを出力するさいに、ループの中で前の行の値を変更したい 1 2022/10/27 14:21
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- PHP 配列の値の更新方法について 1 2022/08/05 09:49
- Visual Basic(VBA) 型が一致しませんとエラー 6 2023/07/06 20:14
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- PHP ファイルの書き込みについて教えて下さい。 1 2023/03/20 12:01
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語 配列の長さの上限
-
先頭アドレスとは何ですか?
-
【速いブラインドタッチ】手を...
-
配列を使わずに、変数名を動的...
-
C++ vectorに配列をプッシュしたい
-
配列をEraseしてもメモリが開放...
-
Functionの戻り値を2次元配列...
-
C# 配列の変数宣言について。
-
2次元配列を戻り値とする関数?
-
ヘッダーファイルからの取り込...
-
C言語 配列の再初期化
-
キーボード配列の違いの対処法
-
配列変数のアドレス
-
if文の判定条件に配列
-
配列で格納したものをmsgboxで...
-
unsigned char の配列で途中で0...
-
配列を含む構造体の初期値について
-
c言語の配列の先頭アドレスが偶...
-
C言語のプログラムについての質...
-
RGB値を画像(PNG・BMPJPEGなど)...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
C# Listを使わずに2次元配列の...
-
【速いブラインドタッチ】手を...
-
配列をEraseしてもメモリが開放...
-
テキストファイルから文字列を...
-
先頭アドレスとは何ですか?
-
配列で格納したものをmsgboxで...
-
複数の選択範囲の行番号を個別...
-
C# 配列の変数宣言について。
-
C++ vectorに配列をプッシュしたい
-
配列を含む構造体の初期値について
-
VBで構造体の配列を関数に渡す...
-
C言語で特定列だけを抽出して配...
-
キーボードのキー配列について
-
ExcelVBAで質問です。離れた二...
-
2次元配列を戻り値とする関数?
-
unsigned char配列への入力の仕方
-
【C言語】配列の中に配列を入れ...
-
Redimした動的配列はEraseする...
おすすめ情報
> またchar型配列のポインタの実体はどこにあるのでしょうか?
質問が変かもしれません。
どこかにchar型配列の実体が作られるのでしょうか?