Reputation: 7184
I have templated class, say:
template <int X>
struct Foo {
static int get() {return X;}
};
I can of course explicitly instantiate the version that I want:
template class Foo<1>;
I want to generate an error at compile time if a second explicit instantiation is attempted.
template class Foo<1>;
template class Foo<2>; // How to error here at compile time?
Is this possible?
I suspect this will need to use some "redefinition" trick to get the linker to catch this if compilation is done in multiple translation units. I can't for the life of me figure out if this is possible, or how to do it.
If there is a way to do this, does it work without explicit template instantiation?
I'm writing an entirely static class library to manage some hardware on a microcontroller that I'm using. I want to make it easy to change a compile time parameter (X
) and am therefore using templates. #define
is not acceptable. constexpr
won't work, how would you #include
a dependent source file?
Specifically, I have an init()
function that can only be run a single time and I'm actually using __attribute__((constructor))
to force it to be run for me before main()
. If some other user of the library were to inadvertently instantiate a second instance, bad things would happen.
Upvotes: 5
Views: 511
Reputation: 39788
I’m pretty sure it’s impossible to do this across translation units as you’d have to generate a non-weak symbol from within a template. Within a translation unit it’s easy:
template<int>
struct Foo {
friend void highlander() {}
};
It’s ill-formed to instantiate Foo
repeatedly in one translation unit because there can be only one definition of highlander
. (Conveniently, it’s impossible to ever refer to the function since it’s visible only to ADL and has no parameters.)
You can of course make it ill-formed across translation units too, by giving each specialization a different return type, but there’s no diagnostic required then and you’ll get none in practice.
Upvotes: 3
Reputation: 1477
You can encapsulate your class template as a private nested class template, then expose as a public member only the instance you want to create :
class Foo {
private:
template<int X>
struct BarImpl {
static int get() { return X; }
};
public:
using Bar = BarImpl<1>;
};
Then you can use it like this :
int i = Foo::Bar::get();
//int j = Foo::BarImpl<2>::get(); // Error, BarImpl is private
Also, since you know only one instance of the template will be created, and you know which one, you can (but of course, do not have to) separate the declaration and the definition of the template in a .hpp and a .cpp file like this :
// Foo.hpp
class Foo {
private:
template<int X>
struct BarImpl {
static int get();
};
static constexpr int Y = 1;
public:
using Bar = BarImpl<Y>;
};
// Foo.cpp
template<>
int Foo::Bar::get() {
return Y;
}
Since X
is not accessible via Foo::Bar
we have to save the parameter somewhere (here in Y
). If you cannot use constexpr
you can just make it const
. This also has the advantage of naming your parameter instead of just having a "magic value".
Upvotes: 1