|
|
|
|
|
by mansr
3480 days ago
|
|
Unix linkers give an error if any symbol is defined in multiple object files, different or not. After all the object files have been combined, remaining undefined symbols are resolved by searching the specified libraries in command line order. Each object from a library that provides a needed definition is selected and its undefined symbols are added to the global list. Some linkers will scan the libraries a second time in order to resolve interdependencies. If at the end of this, the combined set of object files (from command line and from libraries) contain more than one definition for any symbol, an error is raised. The MSVC linker is apparently much more lax in this regard. With the inline semantics defined by C99 there are two solutions to this problem: 1. Use static inline. Any non-inlined calls will result in an instance of the function with internal linkage. Aside from wasting space with multiple copies of the same function it is harmless. As mentioned, an optimising linker might even merge identical functions across compilation units. 2. Use inline without explicit extern. Non-inlined calls result in an undefined external reference to be resolved by the linker with a definition provided elsewhere. The inline definition is only used when the function is actually inlined and never provides a non-inline definition of the function, internal or external. Neither of these approaches result in duplicate definitions with external linkage, so the described problem cannot arise. It's disappointing that Microsoft seem to have chosen the one approach to inline functions that results in this sort of breakage. |
|
No, you are incorrect. The function in question was tagged as inline. Having multiple instances of it is legal. If a Unix linker gave an error on multiple copies of an inline function then that would be a serious bug that would prevent them from linking any non-trivial C++ program.
For inline functions it is only an ODR violation if the two instances are different in an ODR relevant way. The language spec doesn't discuss compiler switches but it is logically clear that /arch:AVX versus not is an ODR violation, whereas /O1 versus /O2 is not. So therefore, even having two different instances of a non-inlined inline function is not necessarily illegal, and must be accepted by a conforming linker.
> Use inline without explicit extern.
I'm not sure why you think that this is a solution. An explicit 'extern' is not needed when defining an inline function. If it is not inlined then the compiler generates a copy that can be referenced. I have worked with gcc, clang, and VC++ and I have never had to explicitly mark an inline function as extern.
You are correct that static inline solves the problem, although I think that this is an ugly solution. And, it is a standard library solution, rather than a toolchain solution, so it doesn't automatically help developers who write their own inline functions.
The best suggestion I heard (on twitter) was name mangling (for non-inlined inline functions) that added an architecture suffix, thus making accidentally calling the wrong architecture function impossible. Much cleaner and more efficient than static inline.