seclan のほえほえルーム

| |

C++ の lambda 関数を関数の引数に渡したときの標準の渡し方は値渡し

・
2020/06/11 []

C++ には C++11 から lambda 関数 (ラムダ関数) といふ物が導入されて、関数のやうに見えるオブジェクトを簡単に作ることができるやうになりました。それでこの lambda 関数オブジェクトを template 関数の引数に渡すことが出来ますが、それが値渡しか参照渡しか調べてみました。

次のやうなコードを作って実験してみました。

  1. #include <cstdio>
  2. template<class t_t>
  3. void exec_f1(t_t f)
  4. {
  5. std::printf("f:%u %p\n",sizeof(f), &f);
  6. f(4);
  7. }
  8. template<class t_t>
  9. void exec_f2(t_t f)
  10. {
  11. std::printf("f:%u %p\n",sizeof(f), f);
  12. (*f)(4);
  13. f->operator()(4);
  14. }
  15. template<class t_t>
  16. void exec_f3(t_t &f)
  17. {
  18. std::printf("f:%u %p\n",sizeof(f), &f);
  19. f(4);
  20. }
  21. int main()
  22. {
  23. int x = 3, xx=10,xxx=12;
  24. auto f = [&x,&xx,&xxx](int y){
  25. std::printf("足し算:%d\n", x+y);
  26. ++x;
  27. };
  28. std::printf("f: %p\n", &f);
  29. exec_f1(f);
  30. exec_f1(f);
  31. exec_f2(&f);
  32. exec_f2(&f);
  33. exec_f3(f);
  34. exec_f3(f);
  35. // exec_f2(&[](int y){std::printf("y:%d\n",y);});
  36. // exec_f3([](int y){std::printf("y:%d\n",y);});
  37. return 0;
  38. }

これを次のやうにしてコンパイル実行してみます。

#clang -olam.exe lam.cpp
#lam
f: 001CFE60
f:12 001CFE18
足し算:7
f:12 001CFE18
足し算:8
f:4 001CFE60
足し算:9
足し算:10
f:4 001CFE60
足し算:11
足し算:12
f:12 001CFE60
足し算:13
f:12 001CFE60
足し算:14
#

と云ふことで、実験結果を見て分かる通り、標準の渡し方は値渡しであることが分かりました。つまり [] で参照する変数が増えれば増えるほどオブジェクトの大きさがかさみ、関数呼び出しごとにそれだけ複製されると云ふことです。

ならばポインタ渡しができるのかと exec_f2(&f) としてみれば、呼び出すことが出来ました。もちろん exec_f2 側ではそれに対応した関数呼び出しをしなくてはなりませんが、実行はできました。ただ、このやり方は exec_f2 での関数呼び出しが少し面倒な表記になります。加へて、このやり方は lambda 関数をリテラル的に書くとエラーになります。

  1. exec_f2(&[](int y){std::printf("y:%d\n",y);});
#clang -olam.exe lam.cpp
lam.cpp:44:10: error: taking the address of a temporary object of type '(lambda at lam.cpp:44:11)' [-Waddress-of-temporary]
        exec_f2(&[](int y){std::printf("y:%d\n",y);});
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
#

それならば、今度は exec_f3 のやうに無理やり参照渡しと設定して呼び出したところ、うまく出来たやうです。ポインタアドレスが一致してゐることから、複製されず、ポインタだけ渡されたやうです。と云ふことで、関数の引数に関数オブジェクトを指定するときは、「&」を付けて引数宣言をしたほうが良いかもしれません。と思ったりしますが、この書き方も次のやうにリテラル的に書くとエラーに成ってしまひます。

  1. exec_f3([](int y){std::printf("y:%d\n",y);});
#clang -olam.exe lam.cpp
lam.cpp:45:2: error: no matching function for call to 'exec_f3'
        exec_f3([](int y){std::printf("y:%d\n",y);});
        ^~~~~~~
lam.cpp:19:6: note: candidate function [with t_t = (lambda at lam.cpp:45:10)] not viable: expects an l-value for 1st argument
void exec_f3(t_t &f)
     ^
1 error generated.
#

と云ふことで結局始めの値渡ししか方法がないことになります。



by seclan

関連


| |

 

配信

21.23 msec