はじめに
Effective C++ 第3版の2項7ページから勉強していきます。
今回は、「マクロで定義する関数より、inline関数を使おう」についです。
Effective C++ 第3版 - 2項 #defineより、const, enum, inlineを使おう -
マクロで定義する関数より、inline関数を使おう
マクロで定義する関数の問題点
#defineを使って定義するマクロは、関数のように働きますが、関数呼び出しのオーバーヘッドがありません。(呼び出し時間が少ない)
しかし、#defineを使ってマクロを定義する場合、注意する必要があります。
以下に、#defineのよくない利用方法を示します。
# define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b));
これは、与えられた2つの引数のうち大きい方を引数にして関数fを呼び出すマクロです。
注意点1つめとして、このようなマクロを書くときには、仮引数を丸カッコで囲まないといけません。
しかし、正しく丸カッコをつけても、次のようなコードが書かれれば、困ったことになります。
CALL_WITH_MAX(++a, b); // aが2回インクリメントされる
CALL_WITH_MAX(++a, b + 10); // aが1回インクリメントされる
上のコードでは、関数fが呼び出される前にaが何回インクリメントされるかは、引数によって変わってしまいます。
なぜか?
CALL_WITH_MAX(++a, b); // aが2回インクリメントされる
では、マクロから関数fに与えられる引数は、
(++a) > (b) ? (++a) : (b)
のようになります。
そのため、(++a) > (b) が True ならインクリメントが2回行われてしまう。
この問題を解決するためにインライン(inline)関数を用います。
インライン関数とは、「in line(行中に)」という言葉が示しているとおり、関数の中身をその部分に埋め込みます。
そのため、インライン関数は、呼び出しにかかる時間が普通の関数を短い利点を持つ。
マクロと違う点は、テキストが置き換わるわけではないということと、戻り値と引数に型があるということです。
インライン関数を用いて、先ほどの「与えられた2つの引数のうち大きい方を引数にして関数fを呼び出す」処理を書くと、
inline void callWithMax(const T& a, const T& b) {
f(a > b ? a : b);
}
のようになります。
このインライン関数を呼び出す処理は、
callWithMax(++a, b); // aが1回インクリメント
callWithMax(++a, b + 10); // aが1回インクリメント
のようになります。
インライン関数を用いることで、仮引数を丸カッコで囲む必要もなく、引数を複数回評価されるといった心配もなくなります。
以下に今回、勉強でしようしたコードと実行結果を示します。
サンプルコード
# include <iostream>
# define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b));
void f(const int& x) { std::cout << __func__ << " : value = " << x << std::endl; }
template <typename T>
inline void callWithMax(const T& a, const T& b) {
f(a > b ? a : b);
}
int main(int argc, char* argv[]) {
std::cout << "2_inline_define.cpp" << std::endl;
int a = 5;
int b = 0;
CALL_WITH_MAX(++a, b); // aが2回インクリメントされる
std::cout << "a : " << a << std::endl;
std::cout << "b : " << b << std::endl;
CALL_WITH_MAX(++a, b + 10); // aが1回インクリメントされる
std::cout << "a : " << a << std::endl;
std::cout << "b : " << b << std::endl;
std::cout << "\n############################################\n" << std::endl;
a = 5;
b = 0;
callWithMax(++a, b); // aが1回インクリメント
std::cout << "a : " << a << std::endl;
std::cout << "b : " << b << std::endl;
callWithMax(++a, b + 10); // aが1回インクリメント
std::cout << "a : " << a << std::endl;
std::cout << "b : " << b << std::endl;
}
実行結果
参考文献
・https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0