I'm studying the behavior of the C++ linker with respect to template specializations. I'm using Microsoft Visual C++ 2010 for these experiments. I don't know if the behavior is the same with other toolchains (e.g. gcc).
Here's a first code snippet:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> int foo<double>() { return 2; }
int bar();
int main()
{
const int x = bar();
const int y = foo<double>(); // doesn't link
}
Expectedly, this code doesn't link because foo<double>()
has multiple definitions as it gets instantiated once in bar.cpp and once in main.cpp (via specialization). We would then expect, if this program would link, that bar()
and main()
would use distinct instantiations of foo()
such that at the end we would have x == 1 and y == 2.
Let's fix the link error by declaring the specialization of foo<double>()
as static
:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> static int foo<double>() { return 2; } // note: static
int bar();
int main()
{
const int x = bar(); // x == 1
const int y = foo<double>(); // y == 2
}
We now have x == 1 and y == 2, as we expected. (Note: we must use the static
keyword here: an anonymous namespace won't do since we can't specialize a template function in a different namespace than its declaration.)
Now, the use of the static
keyword is rather unintuitive. Typically, the specialization foo<double>()
would reside somewhere in a header file, and thus would be marked as inline, like in the following snippet:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; } // note: inline
int bar();
int main()
{
const int x = bar(); // x == 2
const int y = foo<double>(); // y == 2
}
This code now links properly and when we run it we get x == 2 and y == 2. This is the bit I find surprising: why is there a single definition of foo<double>()
? What is the meaning of inline
in this code?
A last snippet:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; } // note: inline
int bar();
int main()
{
const int x = bar(); // x == 1
// const int y = foo<double>(); // note: commented out
}
This case is actually not surprising: the specialization of foo<double>()
is no longer instantiated in main.cpp (although the declaration is still there), so the only instantiation remaining is the one in bar.cpp.