I am developing a C++ library where I need to provide class template to the user.
The template parameter of this class is a reference. However, I am getting a linker error. Here is a minimal test case:
test.hh
#ifndef TEST_HH_
#define TEST_HH_
template <double const& val>
struct A{};
constexpr double zero = 0.;
A<zero> foo();
#endif // TEST_HH_
test.cpp
#include "test.hh"
A<zero> foo(){ return A<zero>(); }
main.cpp
#include "test.hh"
int main()
{
foo();
}
When compiling this piece of code, I get the warning:
'A<zero> foo()' used but never defined
followed by a linker error:
undefined reference to foo()
I tried to replace the double by an int :
template <int val>
struct A{};
and it linked (when passing a int as parameter ofc), but I really need a double.
I also tried a common solution when a template class involves linkage error, I implemented the foo() function in test.hh instead of test.cpp, but I would like to avoid just putting all the code in the header.
Be happy that this didn't link! Could be worse...
The problem is basically that test.cpp and main.cpp both have different objects named ::zero. This is an ODR violation - you have multiple definitions for that variable, and you end up with A<zero> technically being different types since A's template parameter val is a reference to different objects in different translation units.
Hopefully that explanation makes it clear why A<int> works fine.
In C++17, you want:
inline constexpr double zero = 0.;
In C++14, you either need to add a definition in one of the translation units or do some other kind of magic, like having ::zero itself be a reference to some static template variable or other voodoo.