c++templateslinkerc++14non-type

Linker error when using template class with a reference non-type template parameter


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.


Solution

  • 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.