The following is a great simplification of my code:
data.h
#include "id.h"
namespace n1
{
struct data
{
/* Relies on struct id */
};
}
id.h
namespace n1
{
struct data; // Forward declaration
template <int dims>
struct id
{
id(unsigned n);
data operator*(unsigned n) const;
};
template <int dims>
data operator*(unsigned n, id<dims> i);
} // namespace n1
id.cpp
#include "id.h"
#include "data.h"
using namespace n1;
// Force instantiation of struct id for template values 1, 2, and 3
template struct id<1>;
template struct id<2>;
template struct id<3>;
template <int dims>
id<dims>::id(unsigned n) {}
template <int dims>
data id<dims>::operator*(unsigned n) const
{
return data();
}
// warning C4667: no function template defined that matches forced instantiation
template data operator*<1>(unsigned n, id<1> i);
template <int dims>
data operator*(unsigned n, id<dims> i)
{
return i * n;
}
I'm using Visual Studio 2013. The first problem I encounter with this approach is the warning C4667
. An alternative is to declare friend data operator*(unsigned n, id i)
inside struct id
and not forcing instantiation of this operator, which gets rid of the warning, but nothing else.
The real problem occurs when I decide to actually use this operator, e.g. in main():
using namespace n1;
id<1> i(7); // Works fine
data d1 = i * 5; // Works fine
data d2 = 4 * i; // error LNK2019: unresolved external symbol
// struct n1::data __cdecl n1::operator*<1>(unsigned int,struct n1::id<1>
I believe this question to have basically the same problem, however it does not have an answer. My hoping is that my example is much simpler to identify the problem.
I tried all the suggestions from there, trying all combinations that I could find that were able to compile, but none of them were able to link. I guess an alternative would be to split structs id
and data
into multiple structs and files, however that may prove messy at this point.
Edit: The question is definitely not a duplicate of this one. As you can see from my code, I'm asking about explicit specialization for known data types, which I have been successful in implementing in a separate source file for everything except non-member operators. I have to admit that forced instantiation is a new trick for me, which I do not know how to use in this particular example.