こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

締切り済みの質問

inlineとdefineの違い

関数をinlineとして使うとdefineより優れていると聞きました.
ぱっと思いついた感じでは戻り値の型が明確になる感じでしょうか

あとはインクリメントするときになにか弊害が起きそうな気がするのですが,なにかわかりやすい例があれば教えてください.

投稿日時 - 2009-04-01 00:22:19

QNo.4842994

すぐに回答ほしいです

このQ&Aは役に立ちましたか?

15人が「このQ&Aが役に立った」と投票しています

回答(13)

ANo.13

こんにちは
良スレなんで、便乗質問させてください。

LippmanのC++プライマーに
「インライン関数に関数呼び出しのオーバーヘッドはない」
と買いてあるし、大城正典著”詳説C++”(第2版)にも
「inline関数を使うことによって、プリプロセッサマクロを使わなくても、コードをインライン展開できます(…展開しようと努めます)」
とか買いてあるんで、コンパイラがinline関数のインライン展開に成功したら、実装依存とはいうものの、関数マクロをインライン展開したものと、大して違わないものが出来るんだなとか勝手に思ってしまっていました。
 inline指定した関数を、コンパイラがインライン展開しようと努めてインライン展開に成功したものと、
 関数マクロがプリプロセッサでインライン展開されたものとで、
プログラムの実行スピード上、どこらへんがいちばん違うんでしょうか?

投稿日時 - 2009-04-02 15:07:38

ANo.12

#7 の max マクロはまだしも,
#define sqr(x) ((x)*(x))
というマクロだと
sqr(x++)
がいきなり未定義動作になります. このようなことを避けるため, 規格では原則的に「提供する関数をマクロとしても提供することはできるが, (同じ動作であることを保証するため) 原則として引数は 1回しか評価しない」ことを定めています.
以下おまけ:
「展開オーバヘッド」をどのような意味で使っていますか>#11.
普通の用語として考えると, 展開オーバヘッドは
マクロ > inline 関数 > (inline でない) 普通の関数
となるはずです (ここでは「プリプロセッサによるマクロの展開」も含めて考えています. それを含めなければマクロと「inline でない普通の関数」は同じオーバヘッドになりますが, 実際上そのような議論は不要でしょう).
関数呼び出しのオーバヘッドであれば (通常)
通常関数 > inline 関数 > マクロ
となります.

投稿日時 - 2009-04-02 13:11:56

ANo.11

ちょっと違う視点で

マクロ展開は、展開オーバーヘッドが0 (フリプロセッサで置換されるため)
inline関数は展開オーバーヘッドが少ない
通常関数は展開オーバーヘッドが大きい

つまり、オーバーヘッドの大きさは
通常関数 > inline関数 > マクロ展開
となるわけです。

となると、プログラム中で何度も呼ぶような関数は、inline、特に速度が必要なものに関してはマクロを使ったほうが早くなります。
実例では、某強豪オセロプログラムが残り深さ2の探索はマクロでやってます。
シビアに速度が必要となってきた場合は、マクロ展開が一番、という話でした。

以下、サンプルです
実行結果:
Macro 811Inline 1702Nomal 1813
ソース:
#include <stdio.h>
#include <time.h>
#define Max_Macro(a,b) ((a) > (b)? (a):(b))
template <class T>
inline T Max_Inline(const T a, const T b){return a>b?a:b; }
template <class T>
T Max_Nomal(const T a, const T b){return a>b?a:b; }
int main(){
time_t begin, end;
int buf;
// マクロ
begin = clock();
for(int i = 0; i < 100000000; i++)
buf = Max_Macro(i, 1000);
end = clock();
printf("Macro %d", end-begin);
// inline
begin = clock();
for(int i = 0; i < 100000000; i++)
buf = Max_Inline(i, 1000);
end = clock();
printf("Inline %d", end-begin);
// 通常
begin = clock();
for(int i = 0; i < 100000000; i++)
buf = Max_Nomal(i, 1000);
end = clock();
printf("Nomal %d", end-begin);
return 0;
}

投稿日時 - 2009-04-01 21:58:41

ANo.10

#ifndef FOO_H
#define FOO_H
#include <limits>
#include <algorithm>
template <typename T>
inline T foo(T arg) { return std::max(arg, 10); }
#endif

というライブラリのヘッダファイル "foo.h" があったとします。
クライアントコードで、

#include <windef.h>
#include "foo.h"
...

と書くと、<limits>の中でコンパイルエラーが発生します。
これではユーザー(通常アプリ屋)からクレームが出ますので、やむなく、

#ifndef FOO_H
#define FOO_H

#ifdef _MSC_VER
#define NOMINMAX
#include <windef.h>
#undef NOMINMAX
#endif

#include <limits>
#include <algorithm>
template <typename T>
inline T foo(T arg) { return std::max(arg, 10); }
#endif

↑のようにせざるを得ません。
しかし、この場合には、本来不要なはずのTRUEやBYTEが定義されてしまいます。同じライブラリをLinux等で使う場合、これらの識別子は定義されないので移植性の問題が発生します。

もし、

#ifndef FOO_H
#define FOO_H
#undef max
#undef min
#include <limits>
#include <algorithm>
template <typename T>
inline T foo(T arg) { return std::max(arg, 10); }
#endif

とした場合、

#include <windef.h>
#include "foo.h"
...

だと問題がなくても、

#include "foo.h"
#include <windef.h>
...

の場合に問題が出ることがあります。

#9さんのように、

inline T foo(T arg) { return (std::max)(arg, 10); }

とすれば解決するかというとそうでもなく、VC++でSTLportのようなサードパーティ製のライブラリを使った場合など、今度は<algorithm>でコンパイルエラーが発生する可能性が出てきます。

このように、マクロは多くの害悪を撒き散らします。

投稿日時 - 2009-04-01 17:52:11

ANo.9

 こんにちは。
 min/maxについてですが、手法の良し悪しは別として、以下で罷り通ります(VC以外はどうかまでは分かりません)。

#include<windows.h>
#include<algorithm>

int main()
{
int a = 0;
int b = 1;

//windows.h
max(a, b);

//STL
(std::max)(a, b);

return 0;
}

投稿日時 - 2009-04-01 17:13:55

ANo.8

>しかし、これをやると、本来不要なはずのTRUEやFALSEやBYTEなどの
>識別子も定義されてしまい、別の問題が発生します。

何を仰られているのか判りません。

当方は「TRUEやFALSEやBYTEなどの定義が必要でwindef.hをインクルードしているが、maxマクロだけは要らないって状況」を想定しています。

もし「TRUEやFALSEやBYTEなどの識別子も要らず、maxのマクロ定義が要らない。ぶっちゃけ、windef.hをインクルードして欲しくない」って言うなら、最初っから#includeなんかしないし、他のヘッダが勝手にインクルードする恐れがあるなら、ソースの一番最初にでも
#define _WINDEF_
とか書いておけば良いのです。

こう書けば、例えどこかに
#include <windef.h>
って行があっても、windef.hの中身はすべて無視されます。

ぶっちゃけ「TRUEやFALSEやBYTEなどの識別子が不要」ってのが理解出来ません。私は「必要だからインクルードしてんじゃないの?不要ならインクルードしなきゃ良いんじゃ?」って考えてるので、この「本来不要な筈の~~~の識別子」という記述が意味不明で理解できません。

【以下、重要事項】

「当サイトでは、議論を目的とした質問と回答、結果的に議論になった質問と回答は、削除対象」です。

上記の利用規約に抵触する恐れ、大元の質問が回答ごと削除される怖れがあるので、当方のこの回答に対しレスポンス(反応)を付ける回答を投稿するのはご遠慮下さい。

【重要事項ここまで】

投稿日時 - 2009-04-01 15:33:22

ANo.7

>あとはインクリメントするときになにか弊害が起きそうな気がするのですが
>なにかわかりやすい例があれば教えてください

以下の例では、maxマクロの中で「a」「b」は「大きい方は2回評価されるが、小さい方は1回しか評価されない」ので、副作用が起きます。

#include <stdio.h>
#undef max

inline int max(int a,int b)
{
 return ((a) > (b) ? (a) : (b));
}

int main(void)
{
 int ary[4] = {1,2,3,4};
 int mx,*p1,*p2;
 p1 = &ary[0];
 p2 = &ary[1];
#define max(a,b) ((a) > (b) ? (a) : (b))
 mx = max(*p1++,*p2++); //ここはマクロが使用される
#undef max
 printf("mx=%d *p1=%d *p2=%d\n",mx,*p1,*p2);
 p1 = &ary[0];
 p2 = &ary[1];
 mx = max(*p1++,*p2++); //ここはインライン展開される
 printf("mx=%d *p1=%d *p2=%d\n",mx,*p1,*p2);
 return 0;
}

これの実行結果は
mx=3 *p1=2 *p2=4
mx=2 *p1=2 *p2=3
です。1行目がマクロの結果、2行目がインライン関数の結果です。

インライン関数の方は副作用が起きていません。呼び出し前は
*p1=1
*p2=2
ですから、結果のmxは正しく「2」になっていますし、p1、p2は1回だけインクリメントされ、printfの時点で
*p1=2
*p2=3
になっています。

ところが、マクロの方は副作用が起きていて、呼び出し前は
*p1=1
*p2=2
なのに、結果のmxは「3」になっています。しかも「*p2++が2回評価された為」にp2が2回インクリメントされ、printfの時点で
*p1=2
*p2=4
になっています。

インライン関数は「実行コードが呼び出し場所にそのまま展開される」のはマクロと同様ですが、インライン関数は「あくまでも、関数」ですから、渡された引数は、繰り返し何度も評価したとしても、副作用を起こしません。

普通の関数と同じく「呼び出し元で渡す引数と、呼び出し先で受け取る引数はベツモノ」なのです。

投稿日時 - 2009-04-01 15:02:27

ANo.6

> #define NOMINMAX
> #include <windef.h>
> #undef NOMINMAX
> とすれば、何の問題も起きない筈。

そうともいえません。

ライブラリのヘッダファイルで<limits>をインクルードしなければならない場合、上記の方法を適用するには、

#ifdef _MSC_VER
#define NOMINMAX
#include <windef.h>
#undef NOMINMAX
#endif

をヘッダファイルの先頭付近に記述しなければならなくなります。そうしないと、このライブラリのヘッダファイルでコンパイルエラーが発生するとクレームを付けるユーザーが少なからず出てきます。
しかし、これをやると、本来不要なはずのTRUEやFALSEやBYTEなどの識別子も定義されてしまい、別の問題が発生します。

また、上記の方法ではVisual C++とWindows以外の処理系の場合は問題ありませんが、C++ BuilderやMinGWなどのVisual C++以外の処理系で不具合が生じます。
つまり移植性に関して致命的な問題が残るわけです。

投稿日時 - 2009-04-01 14:42:45

ANo.5

>実際,このマクロがWin32 SDKに含まれるWinDEF.hで定義されているため,
>標準C++の<limits>のstd::numeric_limits<T>::max関数や,<algorithm>のstd::max関数がひっかかり,コンパイルエラーになると言う洒落にならない事態も発生していました。

#define NOMINMAX
#include <windef.h>
#undef NOMINMAX
とすれば、何の問題も起きない筈。

windef.h中のmax、minのマクロ定義は、NOMINMAXが定義済みの場合、#ifdefによりスキップされる。

こーいう「汎用的な名前」の定義は、普通「抑止する方法」が必ずある。

投稿日時 - 2009-04-01 14:17:20

ANo.4

#3 に挙がっている max なんかは「マクロで書いたほうがきれいになる」典型例でしょうか. C ではジェネリックな関数が書けないのでしょうがない. C++ でも, 普通に
template <class T> T max(const T a, const T b) { return a>b ? a : b; }
と定義すると, max(4, 5.9) がマッチしないので意外と苦心したりします.
ただ, 確かにこんな定義をシステムが提供するヘッダにするってのはいかがなものかと思いますね. せめて
#define _max(x,y) ((x)>(y)?(x):(y))
だろこのボケ, って感じでしょうか. いまだに (製品版では) C99 に対応してないからなぁ....
なお, 厳密にいえば
#define max(x,y) ((x)>(y)?(x):(y))
というマクロ定義があったときに「maxという識別子を一切使えません」ということはないです.
このマクロ定義をすると, プリプロセッサは「max というトークンの後に ( というトークンがある場合」にマクロを展開します. だから, 例えば
int max;
max = max(4, 6);
という文は (意味はありませんが) 全く正しくコンパイルできます. これは, 逆にインライン関数ではコンパイルエラーになるはず.

投稿日時 - 2009-04-01 12:01:21

ANo.3

#defineは文字列の置換でしかない上,C的な意味での名前空間すら持ちません。
そのため,例えば
#define max(x,y) ((x)>(y)?(x):(y))
といった典型的なマクロがあった場合,そのマクロを含むファイルをインクルードしたが最後,その翻訳単位中ではmaxという識別子を一切使えません。

実際,このマクロがWin32 SDKに含まれるWinDEF.hで定義されているため,
http://msdn.microsoft.com/en-us/library/ms709458.aspx
標準C++の<limits>のstd::numeric_limits<T>::max関数や,<algorithm>のstd::max関数がひっかかり,コンパイルエラーになると言う洒落にならない事態も発生していました。
example) http://ml.tietew.jp/cppll/cppll_novice/article/831

基本的に,C++ではinline関数やconstを使うのが普通ですし,
Cでもinline関数にすることをお勧めします。
# 10年前に策定された,C99に対応していないコンパイラではそうもいきませんが……。

投稿日時 - 2009-04-01 01:26:00

ANo.2

 以下の記述が参考になるのではないかと思います。

「 Inline functions replace the usual macros in C and have an important advantage : inline functions are not 'blind' text replacements.
From the point of view of semantics, they are not different from normal functions.
As with functions, a type checking occurs and the same scopes exist.
There are no side effects caused by a missing bracket.
As far as the semantics of a program are concerned, it makes no difference whether functions are declared inline or not.
Only running time is influenced. 」 (Nicolai M.Josuttis, "Object-oriented Programming in C++", p.174 より)

投稿日時 - 2009-04-01 01:19:26

ANo.1

defineは関数じゃないで、
用途が違うので、優越を比べるような類のものではないと感じてます。

投稿日時 - 2009-04-01 00:36:09

あなたにオススメの質問