関数と関数ポインタは配列とポインタの関連に似ている。
配列とポインタ
配列は関数の引数になるとポインタに変わる。下の2つの関数aとbは同じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <typeinfo> void a(char *p) { std::cout << typeid (p).name() << std::endl; } void b(char p[]) { std::cout << typeid (p).name() << std::endl; } int main() { char sz[10]; a(sz); b(sz); char* p = sz; a(p); b(p); } |
このように配列がポインタに変わる動作をdecayと呼ぶ。
関数と関数ポインタ
下の2つの関数aとbも同くdecayする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <typeinfo> void a(char(*func)()) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } void b(char func()) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } char func() { return 'a'; } int main() { a(func); b(func); } |
参照はdecayしない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> #include <typeinfo> void a(char *&p) { std::cout << typeid (p).name() << std::endl; } void b(char (&p)[10]) { std::cout << typeid (p).name() << std::endl; } int main() { char sz[10]; // error C2664: 'void a(char *&)': cannot convert argument 1 from 'char [10]' to 'char *&' (vc) // error: cannot bind non-const lvalue reference of type 'char*&' to an rvalue of type 'char*' (gcc) // a(sz); b(sz); char* p = sz; a(p); // error C2664: 'void b(char (&)[10])': cannot convert argument 1 from 'char *' to 'char (&)[10]' (vc) // error: invalid initialization of reference of type 'char (&)[10]' from expression of type 'char*' (gcc) //b(p); } |
関数bは配列のサイズも合ってないとエラーになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <typeinfo> typedef char (*FUNCP)(); void a(FUNCP &func) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } typedef char FUNC(); void b(FUNC& func) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } char func() { return 'a'; } int main() { // error C2664: 'void a(FUNCP &)': cannot convert argument 1 from 'char (__cdecl *)(void)' to 'FUNCP &' (vc) // error: cannot bind non-const lvalue reference of type 'char (*&)()' to an rvalue of type 'char (*)()' (gcc) // a(func); FUNCP myfuncp=func; a(myfuncp); b(func); // error C2072: 'myfunc': initialization of a function (vc) // error: function 'char myfunc()' is initialized like a variable (gcc) // FUNC myfunc=func; } |
結論
値渡しはdecayするが参照渡しはdecayしない。これにtemplateが絡むとすごく複雑になる。