C++’s One-Definition-Rule roughly states that
In the entire program, an object or non-inline function cannot have more than one definition; if an object or function is used, it must have exactly one definition.
Which sounds like a good idea – until reality kicks in with all its hairy details.
How, for example, is it possible to overload global new(), or many other CRT overload-able functions? If a function was decorated as inline but the optimizer decided not to inline it (a very common scenario) – it is included in multiple translation units. Can a linker possibly handle that without breaking the ODR?
Enter weak symbols. In a nutshell:
During linking, a strong symbol can override a weak symbol of the same name. In contrast, 2 strong symbols that share a name yield a link error
Symbol, of course, can be either a function or extern variable. Unlike (most?) other compilers, VC++ does not expose an explicit way of declaring symbols as weak – but there are two alternatives that come close:
- __declspec(selectany), which directs the linker to select just one (any one) of multiple definitions for the symbol and discard the rest. MS explicitly state this as a quasi-answer for not exposing weak references to the programmer, but as a commenter notes this is not satisfying – one could hope to be able to declare a single implementation as *strong*, thus enforcing its selection at build time.
- The undocumented #pragma /alternatename, found in CRT sources and mentioned in this StackOverflow answer. This one helps mimic a different weak-symbol functionality: initializing the symbol to zero if no definition is found. This also hardly suffices as a replacement.
The VC++ toolchain does use weak symbols internally (i.e., the compiler generates them and the linker consumes them). You can inspect which symbols were treated as weak by running dumpbin /SYMBOLS on an obj file. Typical output would be –
Section length 8C, #relocs E, #linenums 0, checksum 9CA493CF, selection 5 (pick associative Section 0xA6) Relocation CRC 4EF609B6 2B8 00000000 SECTAA notype Static | __ehfuncinfo$??0MyClass@@QAE@XZ 2B9 00000024 SECTAA notype Static | __unwindtable$??0MyClass@@QAE@XZ 2BA 00000000 UNDEF notype () External | __purecall 2BB 00000000 UNDEF notype () External | ??_GMyClass@@UAEPAXI@Z (public: virtual void * __thiscall MyClass::`scalar deleting destructor'(unsigned int)) 2BC 00000000 UNDEF notype () WeakExternal | ??_EMyClass@@UAEPAXI@Z (public: virtual void * __thiscall MyClass::`vector deleting destructor'(unsigned int))
Note the WeakExternal tag in the last line.
This snippet isn’t entirely random – it demonstrates another problem with choosing not to expose weak linkage to users: what do you do with compiler generated functions? Stay tuned.