mailgerigk mailgerigk - 17 days ago 7
C++ Question

MSVC 2017 creates copies of template function in shared libraries

While trying to replicate the behavior in this question in Visual Studio 2017 I found that instead of linking

&FuncTemplate<C>
to the exact same address the function
template<> FuncTemplate<C>() {}
gets copied into dllA and dllB so that the corresponding test program always returns
not equal
.

The solution was setup fresh with 3 Win32Projects, one as ConsoleApplication, the others as DLL. To link the DLLs I added them as reference to the console project (linking manually didn't work either). The only change in code I made was adding the
__declspec(dllexport)
to
a()
and
b()
.

Is this behavior standard conforment? It seems like the ODR should be used here to collapse the copies of the function. Is there a way to get the same behavior seen in the other question?

Template.h

#pragma once

typedef void (*FuncPtr)();

template<typename T>
void FuncTemplate() {}

class C {};


a.cpp - dll project 1

#include "Template.h"

__declspec(dllexport) FuncPtr a() {
return &FuncTemplate<C>;
}


b.cpp - dll project 2

#include "Template.h"

__declspec(dllexport )FuncPtr b() {
return &FuncTemplate<C>;
}


main.cpp - console project

#include <iostream>

#include "i.h"

// seems like there is no __declspec(dllimport) needed here
FuncPtr a();
FuncPtr b();

int main() {
std::cout << (a() == b() ? "equal" : "not equal") << std::endl;

return 0;
}

Answer

C++ compilation is generally split into two parts, the compiler itself and the linker. It is the job of the linker to find and consolidate all the compilations of an identical function into a single unit and throw away the duplicates. At the end of a linking step, every function should either be part of the linker output or flagged as needing to be resolved at execution time from another DLL. Each DLL will contain a copy of the function if it is being used within that DLL or exported from it.

The process of resolving dynamic links at execution time is outside of the C++ tool chain, it happens at the level of the OS. It doesn't have the ability to consolidate duplicates like the linker does.

I think as far as ODR is concerned, each DLL is considered a separate executable.