• 締切済み

inlineとdefineの違い

関数をinlineとして使うとdefineより優れていると聞きました. ぱっと思いついた感じでは戻り値の型が明確になる感じでしょうか あとはインクリメントするときになにか弊害が起きそうな気がするのですが,なにかわかりやすい例があれば教えてください.

みんなの回答

  • mha01
  • ベストアンサー率81% (9/11)
回答No.13

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

全文を見る
すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.12

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

全文を見る
すると、全ての回答が全文表示されます。
回答No.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; }

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.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>でコンパイルエラーが発生する可能性が出てきます。 このように、マクロは多くの害悪を撒き散らします。

全文を見る
すると、全ての回答が全文表示されます。
回答No.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; }

全文を見る
すると、全ての回答が全文表示されます。
  • chie65535
  • ベストアンサー率43% (8561/19458)
回答No.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などの識別子が不要」ってのが理解出来ません。私は「必要だからインクルードしてんじゃないの?不要ならインクルードしなきゃ良いんじゃ?」って考えてるので、この「本来不要な筈の~~~の識別子」という記述が意味不明で理解できません。 【以下、重要事項】 「当サイトでは、議論を目的とした質問と回答、結果的に議論になった質問と回答は、削除対象」です。 上記の利用規約に抵触する恐れ、大元の質問が回答ごと削除される怖れがあるので、当方のこの回答に対しレスポンス(反応)を付ける回答を投稿するのはご遠慮下さい。 【重要事項ここまで】

全文を見る
すると、全ての回答が全文表示されます。
  • chie65535
  • ベストアンサー率43% (8561/19458)
回答No.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 になっています。 インライン関数は「実行コードが呼び出し場所にそのまま展開される」のはマクロと同様ですが、インライン関数は「あくまでも、関数」ですから、渡された引数は、繰り返し何度も評価したとしても、副作用を起こしません。 普通の関数と同じく「呼び出し元で渡す引数と、呼び出し先で受け取る引数はベツモノ」なのです。

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.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++以外の処理系で不具合が生じます。 つまり移植性に関して致命的な問題が残るわけです。

全文を見る
すると、全ての回答が全文表示されます。
  • chie65535
  • ベストアンサー率43% (8561/19458)
回答No.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によりスキップされる。 こーいう「汎用的な名前」の定義は、普通「抑止する方法」が必ずある。

全文を見る
すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.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); という文は (意味はありませんが) 全く正しくコンパイルできます. これは, 逆にインライン関数ではコンパイルエラーになるはず.

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 関数ヘッダ?

    {コメントで関数の働きや、引数や戻り値(=関数の返す値)これらの関数を説明するコメント文を関数ヘッダと読んだりもします。} とネットでは書いてあったのですが実際にはどんな感じで書くのかよくわかりません。そしてこれをどこに書けばいのでしょうか? 例としてはこんな感じですか?(多分というか絶対間違ってると思いますが) 関数ヘッダの記述は自由でいいと聞きました。 :関数ヘッダ 関数名 : main   型  :型の動きを書く      引数  : 引数の動きを書く   戻り値 : 0   履歴 :名前  修正内容 日付

  • 戻り値と返り値の違い

    タイトルの通りの質問内容です。 僕が調べた限りは「同じ」と言う意見が多数でした。 中には ・参照渡しした引数の関数通過後の結果が戻り値、  関数自体が返すのが返り値 とか ・引数なしの関数で戻るのが戻り値、引数ありの関数で  戻るのが返り値 とかはたまたその逆とか、混沌としています。 気になります。

  • VB 関数

    初めまして。 VB初心者なので,簡単な質問なのかしれませんが御願いします。 C言語で,   int      main        (void) 戻り値の型    関数名      戻り値なし と書きますが,これと同じ意味を持つプログラムをVBで書くとどのようになりますか? 関数の作り方がまだわかってないので教えていただけますか? 宜しく御願いします。 

  • mallocの書式について

    初心者です。プログラム上でメモリを確保するmallocという関数がありますが書式がいまいち良くわかりません・・。   戻り値? * malloc(確保する型 * 確保する数) のように書いてますけどmallocの前のはやはり戻り値?そのまえの*は?いまいちわかりません。よろしくお願いします。

  • ワンデー アキュビューディファインの購入で迷ってます

    現在ハードコンタクトレンズ使用者です。試供品でもらった「アキュビュー(ディファインでない)」を使用して、楽だったので普通の分(2000円を左右二箱分)買う事にしました。週に1回程度しか使わないので 6ヶ月位かけてゆっくり使うつもりです。 色々、レンズについて調べてると、ディファインというレンズがあるのを知りました。1箱あたり1000円も高いです。しかし、目に力が入るとか、潤んでる感じになるとか・・・たまにそういうクチコミを見ますが、それは、本当なのでしょうか?(=ぱっとみてわかりますか?、するメリットは大きいですか?) あと、使用感が気になります。アキュビューと同じならいいですが、色素の成分とか使ってるせいで、普通のアキュビューより使用感が落ちるなら辞めておこうと思いますが。 どなたか、使った事のある方、もしくは詳しい方で アドバイスを頂けたら助かります。

  • 関数の定義

    関数の定義とは ここから 戻り値の型 関数名(引数リスト) {   文;   ・・・   return式; } ここまで のことをいうのでしょうか??あたりまのような質問ですみません。 参考書には、「関数の定義はブロック内にまとめて記述します」 となっているので{ }の中のものだけなのかと思ってしまいました。 初歩的な質問ですがよろしくお願いします。

  • Delphiでの関数の戻り値を別の型で複数に。

     どうお過ごしですか、ご主人様? 少し困っている事があるんですぅー。 Delphiで関数の戻り値を別の型で、複数にしたいのですが、どうしたらいいのでしょうか? そもそもこういう場合は別の関数に分けた方がいいのでしょうか? 内容は数十行しかなく、まとめたい処理なんです。 グローバル変数は、なんかかっこ悪いので使いたくないし、クラスはまだよく分からないし。 あ、でもでも、普通は関数を分けるんでしょうか? よろしくお願いします。

  • mysql_connect関数の使い方

    mysql_connect関数の、戻り値の使い方についての質問です。 mysql_connectで検索すると、 【使い方例】 $link = mysql_connect('localhost', 'mysql_user', 'mysql_password'); mysql_close($link); みたいなのが見つかります。(エラー処理は省略) そこで、mysql_connectとmysql_closeの間に、クエリを処理する関数を入れるとします。 【例】 $link = mysql_connect('localhost', 'mysql_user', 'mysql_password'); test();←この関数の処理についての質問です。 mysql_close($link); test(){ mysql_query(”sql文”);} 上記の例の test()関数についてですが、mysql_connectの戻り値の$linkを渡さなくてもいいのでしょうか? いつもは、test($link)見たいにしていました。 しかし、今回は戻り値を渡すのを忘れていたのに、ちゃんと希望通りに動いていました。 今回のように、mysql_connect関数をひらいて、dbの処理をする場合は、 途中の関数に、戻り値を渡さなくてもいいのでしょうか?

    • ベストアンサー
    • PHP
  • 基本に戻って、型について考えてみる

    すいません。 サーブレットの所で質問なんですけれども、 戻り値の型が基本型やString型の場合だったら、 変数にどんな値が格納されているのかイメージしやすいんですけれども(例えばすぐ下の例の場合ですね)、戻り値の型がString型以外の参照型の場合には、戻り値が具体的にどのような値になっているのかイメージできなくて困っています。 String str1=br.readLine(); int num1=Integer.parseInt(str1); 例えばenumeration型。 一体この型はどんな型?っていう質問もへんなんですけれども、この型の変数がさしている値というのは具体的にいうとどう表現できるのでしょうか? 基本型やString型は本当に分かりやすいのですけれど。 あと他にもPrintWriter型だとか、Object型だとか、 なぜかイメージできないんですよね。 Enumeration paramNames = request.getParameterNames(); もしよろしければ教えてください。 お願いします。

    • ベストアンサー
    • Java
  • Objective-cの型の初期化について質問です。

    Objective-cの型の初期化について質問です。 たとえば次のようなfloat型の戻り値関数を定義していて、 ----------------- - (float) getMoveSize { float moveSize = 1.1f; return moveSize; } ----------------- 関数の戻り値を取得しようとするとコンパイルエラーになります。 ----------------- float height = [self getMoveSize]; //→error: incompatible types in initialization ----------------- どのようにすればよいか教えて下さい。 float height = 0.0f; height += [self getMoveSize] みたいなことをやりたいだけです。

このQ&Aのポイント
  • os bigsur EP-805A です。ディスクレーベルの選択枠の色が違い、選べません。
  • EPSON社製品EP-805Aにおいて、Big Sur OSを使用しているとディスクレーベルの選択枠の色が異なり、正常に選択できない問題が発生しています。
  • EP-805AのOSがBig Surの場合、ディスクレーベルの選択枠の色が正常ではなく、選択することができません。
回答を見る