问题描述
这里是几份C++代码:
a.h
#include <iostream>#ifndef STR#define STR 'default'#endifstruct 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),还是未定义行为呢?
问题解答
回答1:试了一下,gcc 如果 -O2 或者 -O1 优化,出来的结果 imp1imp2 这样的,如果是 -O0 优化,出来的 imp1imp1 或者 imp2imp2
原因应该是在没有 -O 优化的情况下 inline 会被 gcc 忽略,以方便调试。
标准里 inline 应该是一个 suggestion,而不是一个强制的要求。如果没有 inline,.o 文件的顺序就成了决定性因素。
$ gcc -v使用内建 specs。COLLECT_GCC=gccCOLLECT_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-languages=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线程模型:posixgcc 版本 6.1.1 20160802 (GCC) 回答2:
应该是GCC没有进行inline(即使你写了inline,编译器也不保证进行inline),这样的话,到了链接阶段,对于A::print的引用,用哪个.o文件中的A::print都有可能(都是弱符号),但一定只会使用一个。
回答3:楼主需要搞清楚编译的过程,1.预处理(Preprocessing)2.编译(Compilation)3.汇编(Assembly)4.链接(Linking)

define在预处理阶段inline在编译阶段
这样应该明白了吧
回答4:环境:
Apple LLVM version 8.0.0 (clang-800.0.38)Target: x86_64-apple-darwin16.0.0Thread model: posix
clang -O0 -O1 的结果是 imp1imp1clang -O2 -O3 的结果是 imp1imp2
我将代码修改了一下,强制使用内联:
struct A { __attribute__((always_inline)) static void print() { std::cout << STR; }};
在任何优化开关下都是 imp1imp2
而当代码强制禁止内联的时候:
struct A { __attribute__((noinline)) static void print() { std::cout << STR; }};
结果与文件顺序相关:如果 imp1.cpp 排在前面,在任何优化开关下都是 imp1imp1;如果 imp2.cpp 排在前面,在任何优化开关下都是 imp2imp2;
解释如下:强制内联时,内联函数的宏会在在两个文件中被独立的替换;禁止内联时,内联函数的宏会在第一个编译单元被替换(因为如果在多个编译单元被替换就等于内联了)。未强制的时候(无 __attribute__)在 -O0 -O1下,编译器会关闭内联,而 -O2 -O3 下才打开。
除了成员函数之外,如果是普通内联函数(non-member,non-static-member)定义在头文件里,会引起编译错误,因为重定义;而定义在 cpp 里则根本不会被其他文件的宏所影响。

