検索

ホームページ  >  に質問  >  本文

如果C++的inline函数中有宏,行为是怎样的?

这里是几份C++代码:

a.h

#include <iostream>
#ifndef STR
#define STR "default"
#endif

struct A {
    static void print() { std::cout << STR; }
};

imp1.cc

#define STR "imp1"
#include "a.h"
void imp1() { A::print(); }

imp2.cc

#define STR "imp2"
#include "a.h"
void imp2() { A::print(); }

main.cc

void imp1();
void imp2();
int main() { imp1(); imp2(); }

我们知道,在类定义中定义的函数默认是inline函数,inline函数将建议编译器在行内展开代码。那么main打印的是imp1, imp2还是str?

我找不到相关的资料。相同的代码,编译命令为g++ main.cc imp1.cc imp2.cc时,在GCC 6.1.1上面是打印imp1imp2,在MinGW GCC 4.8.1上面是打印imp1imp1。在编译命令为g++ imp2.cc imp1.cc main.cc,在GCC 6.1.1上面没有变化,在MinGW GCC 4.8.1上面是打印imp2imp2

请问C++ standard有相关规定(也就是说是MinGW的bug),还是未定义行为呢?

天蓬老师天蓬老师2805日前632

全員に返信(4)返信します

  • PHP中文网

    PHP中文网2017-04-17 14:41:14

    試してみました。gcc を -O2 または -O1 で最適化すると、結果は imp1imp2 になります。-O0 で最適化すると、結果は imp1imp1 または imp2imp2 になります。

    その理由は、デバッグを容易にするために -O 最適化を行わないと、インラインが gcc によって無視されるためです。

    標準のインラインは、必須要件ではなく提案であるべきです。インラインを使用しない場合、.o ファイルの順序が決定要因になります。

    $ gcc -v
    組み込み仕様を使用します。
    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/lto-wrapper
    ターゲット: x86_64-pc-linux-gnu
    設定は次のとおりです:/build/gcc-multilib/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man -- infodir =/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-messages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with -isl --enable-__cxa_atexit --disable-libunwind-Exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable- gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release
    スレッドモデル: posix
    gcc バージョン 6.1.1 20160802 (GCC) 

    返事
    0
  • 大家讲道理

    大家讲道理2017-04-17 14:41:14

    GCC はインラインで実行されないはずです (たとえインラインで記述したとしても、コンパイラーはリンク段階で A::print への参照を実行することを保証しません)。 .o ファイルは A:: 印刷可能 (すべて弱いシンボル) に使用されますが、使用されるのは 1 つだけです。

    返事
    0
  • 大家讲道理

    大家讲道理2017-04-17 14:41:14

    投稿者はコンパイル プロセスを理解する必要があります。
    1. コンパイル
    3. リンク

    < /p>

    define は前処理段階にあります
    inline はコンパイル段階にあります

    これで明らかになるはずです

    返事
    0
  • 黄舟

    黄舟2017-04-17 14:41:14

    環境:

    Apple LLVM バージョン 8.0.0 (clang-800.0.38)
    ターゲット: x86_64-apple-darwin16.0.0
    スレッドモデル: posix

    clang -O0 -O1 の結果は imp1imp1 になります
    clang -O2 -O3 の結果は imp1imp2 になります

    インライン化を強制するようにコードを変更しました。

    struct A {
        __attribute__((always_inline)) static void print() { std::cout << }
    };

    最適化スイッチでは imp1imp2

    です。

    コードがインライン展開を強制的に禁止している場合:

    struct A {
        __attribute__((noinline)) static void print() { std::cout << }
    };

    結果はファイルの順序に関連しています。
    imp1.cpp が最初にある場合、最適化スイッチでは imp1imp1 になります。

    code> imp2.cpp が最初にランク付けされ、最適化スイッチの下では imp2imp2 になります。

    説明は次のとおりです。
    インライン化が強制されている場合、インライン関数のマクロは 2 つのファイルで独立して置き換えられます。
    インライン化が無効な場合、インライン関数のマクロは次のようにコンパイルされます。最初のユニットが置き換えられます (複数のコンパイル単位で置き換えられた場合、インライン化と同等になるため)。
    強制されない場合 (__attribute__ がない場合)、コンパイラは -O0 -O1 でインライン化をオフにし、-O2 -O3 でのみインライン化をオンにします。

    メンバ関数を除き、通常のインライン関数(非メンバ、非静的メンバ)をヘッダファイルに定義すると再定義によりコンパイルエラーが発生しますが、再定義した場合はコンパイルエラーは発生しません。 cpp で定義されている 他のファイルのマクロの影響を受けます。

    返事
    0
  • キャンセル返事