C++相談室 part56

このエントリーをはてなブックマークに追加
892デフォルトの名無しさん
配列の長さ(要素数)をコンパイル時に計算するにはどうしたらよいですか?
コンパイラの最適化レベルによらずにコンパイル時に計算になる例が欲しいです。

よろしくおねがいします。
893デフォルトの名無しさん:2007/09/23(日) 22:27:25
>>892
T array[Size];
std::cout << sizeof(array) / sizeof(* array);
894デフォルトの名無しさん:2007/09/23(日) 22:31:21
確かにsizeof使うと配列全体のサイズが得られるから、
各要素のサイズで割れば要素数が得られる。
でもsizeofがポインタ変数のサイズを返さないで配列全体のサイズを返すのって文法として正しいの?
コンパイラ依存じゃないよね?
っつーか内部でどうやってんだろ
895デフォルトの名無しさん:2007/09/23(日) 22:36:15
>>894
sizeofはコンパイル時に評価するのだから、オペランドが配列かポインタかは判別できるだろう。
ポインタならポインタのサイズだし、配列なら配列全体のサイズになるだけ。
896デフォルトの名無しさん:2007/09/23(日) 22:37:20
>892
最適化前提でいいなら、
template<class T, int N> int len(T (&)[N]){return N;}
というのがわりと有名。

>892,895
sizeofを使うマクロに比べてこの関数がよいのは、配列以外をこの関数に
与えるとコンパイルエラーになること。

最適化無しで数値にできる方法は知らない。
Boostスレのほうがいいかもな。
897894:2007/09/23(日) 22:45:35
>>895 >>896
なるほど。勉強になりました。
ありがとうございます。
898デフォルトの名無しさん:2007/09/23(日) 22:50:42
>>894
コンパイラ依存ではない

基本的にarrayは宣言以外はポインタだが
sizeof演算子のオペランドの場合は配列になる。
899892:2007/09/23(日) 22:54:17
>>893
ありがとうございます。
これでもいいんですが、できれば array が配列名以外の場合(ポインタとか)の場合に
コンパイルエラーになってほしいです。

boostスレ逝ってきます
900デフォルトの名無しさん:2007/09/23(日) 22:55:21
>>892
この len いいね。

最適化〜のくだりが分からないんだけど、何か問題があるのですか?
901900:2007/09/23(日) 22:56:15
ミス
>>892 x
>>896 o
902デフォルトの名無しさん:2007/09/23(日) 22:57:22
インライン展開されなかったら、関数呼び出しになってしまうってことじゃね?
903デフォルトの名無しさん:2007/09/23(日) 23:02:30
#include <cstddef>
template<typename T, size_t N> char (&lengthof_helper_char_array(T (&a)[N]))[N];
#define lengthof(a) (sizeof(lengthof_helper_char_array(a)))
904デフォルトの名無しさん:2007/09/23(日) 23:03:06
>>900
static int g_x=len(g_array);
としたときに、デバッグ時などで最適化をかけないでコンパイルすると、g_xは
len関数呼び出しによって動的に初期化されることになってしまう。sizeofの割
り算だとそういうことにはならない。すると、初期化のコストはともかく、g_x
が未初期化(0)の状態で(他の翻訳単位のグローバル変数の初期化などで)参照さ
れてしまうケースが生じるかもしれず、マズ-。

ということだと思われ。グローバル変数関係ないんだったら、lenをバシバシ
使えばよさげ。
905903:2007/09/23(日) 23:09:17
sdt::size_t にしたほうが正しいな。

ぐぐればすぐ見つかると思ってたけど意外と見つからなかったんで
ここにメモがき。
906892:2007/09/23(日) 23:10:19
>>893-904
どうもありがとうございます。

>>903がよさそうなんですが、読めません。。。
簡単な解説をいただけませんか?
907デフォルトの名無しさん:2007/09/23(日) 23:13:03
バッファオーバーフローで特定のプログラムを実行って実際にはどうやるの?

908900:2007/09/23(日) 23:18:02
>>902,>>904 thx
そうか、あくまで関数だってこと忘れてた。
グローバル変数については、「初期化の順序を勝手に想定するな」みたいなことは本で読んだことがあるよ。
自分流に書き直したバージョン載せてみる。
template<class T, std::size_t N> inline std::size_t len(const T(&)[N]){return N;}
909デフォルトの名無しさん:2007/09/23(日) 23:18:36
やめとけ
910892:2007/09/23(日) 23:24:35
((((テンプレート関数)へのポインタ)の配列)のsizeof)
かー。わかりました。
911デフォルトの名無しさん:2007/09/23(日) 23:28:02
>>910
いやちがう,910だとしたら
template<typename T, size_t N>
char (*lengthof_helper_char_array(T(&)[N]))[N];
だ。&lengthof_... というのはいったい?
912903:2007/09/23(日) 23:29:10
>>906
関数テンプレートの引数については >>896 といっしょね。
903 は戻り値で値を返すんじゃなくて元の配列と同じ長さの char 型の配列を
返すように宣言してる。 sizeof(char) は 1 なので、この配列に sizeof を
かければ元の配列の長さが得られる。 sizeof の引数の式は実際に実行される
ことがないので、定義は要らない。

typedef とか使ってもっと簡単なコードにしたいところだけど、 template のせいで
同じ記述を何度か繰り返すことになり、あんまりすっきりしない。
913デフォルトの名無しさん:2007/09/23(日) 23:33:24
テンプレート引数で指定した型を継承するやり方で教えて欲しいことがあります。
C++ではテンプレート引数で指定した型に固有のメソッドを呼び出すことは文法的に正しいのでしょうか?
例えば
struct Printable {
void print() { std::cout << "hello" ; } } ;

template <typename T> class Foo : public T {
public: void echo() { print() ; } } ;

と定義した時に、Foo<Printable>のオブジェクトがPrintableのメソッドを呼ぶのはC++の文法として正しいのでしょうか?
cygwinのg++だと怒られるのですが、solarisのCCだと素通りされるので正しく書いているのかよくわからないのです。
914デフォルトの名無しさん:2007/09/23(日) 23:33:35
>>911
&lengthof_... は配列への参照を返すようにしてる。 C/C++ で配列を返す関数は
宣言できないから。で、参照でも sizeof に渡せば同じことになってくれる。
915デフォルトの名無しさん:2007/09/23(日) 23:36:15
>>913
http://gcc.gnu.org/gcc-3.4/changes.html
↑の "unqualified names will no longer find members of a dependent base" でしょ。

this->print() にしてみたらどう?
916デフォルトの名無しさん:2007/09/23(日) 23:42:01
>>914
ありがとうございます。
もうすこしだけ教えていただけませんか?

理解の為、まず
char c3[] = {1,2,3};
char (&rc3)[3] = c3;
int main(){}
こう書いてみたらこれはコンパイルできました。

次に、
char (&f(int))[3];  // これは何.....
int main(){}
こう書いてみたら、これもコンパイルできてしまったんですが、
私は一体何を宣言したことになるのでしょうか?
917デフォルトの名無しさん:2007/09/23(日) 23:45:03
>>912
普通、配列を返す関数は作れないよね?
この宣言が許されるのが不思議だ...。
918912:2007/09/23(日) 23:47:23
あ、「参照を」返すのか!
919918:2007/09/23(日) 23:48:12
ミス。おれは>>917だった
920913:2007/09/23(日) 23:49:13
thisつけたら解消しました!
どうもありがとうございました
921デフォルトの名無しさん:2007/09/23(日) 23:51:12
>>918-919
何がわかったの?

916の実験続き
char (&f(int))[3];
int main() {
  return sizeof(f(10000));
}
は3が戻った。

次に、
char (&f(int))[3];
int main() {
  f(100);
としてみたら、
undefined reference to `f(int)'
でリンクできなかった。とりあえずここまで。
922デフォルトの名無しさん:2007/09/23(日) 23:53:50
こうしたらリンクできた。なんてこった。
こんな関数の定義方法があるのか!企画のどこに書いてあるんだろう?

char (&f(int))[3] {
  static char x[3];
  return x;
}

int main() {
  f(100);

びっくりしました。ありがとうございました。みなさま。
923デフォルトの名無しさん:2007/09/24(月) 00:05:07
これをポータブルにする試みがBoostでされていた気がする
924デフォルトの名無しさん:2007/09/24(月) 00:06:12
>>923 へ?コンパイラに依存なんてしてないでしょ?
925>>917-919:2007/09/24(月) 00:08:19
おれが理解したのはこんな感じ
1.関数は配列を返すことは出来ないが、配列の参照ならば返すことができる。
その宣言方法は
char (&f())[3]; //char[3]の参照を返す関数fを宣言。

2.sizeof に関数呼び出しを与えても、関数呼び出しを行わない。返り血のサイズを返すのみ。
sizeof(f()); //この値は 3

これをtemplateで応用して上記1の配列サイズ(3)をNとし、Nはテンプレートのパラメータにする。
さらに、任意の型で仕様できる用に、typename Tもパラメータに加え、関数fの引数に与えオーバーロードさせる。
使いやすい様に sizeof(〜)全体を囲って #defineすれば>>903の出来上がり。

こんな形で実体の関数が使えるなんて知らなかった。sizeofめ。