views:

184

answers:

3

This is a followup to http://stackoverflow.com/questions/2050900/c-templates-prevent-instantiation-of-base-template

I use templates to achieve function overloading without the mess of implicit type conversions: declare the function template, define desired specializations (overloads). all is well except wrong code does not produce errors until the link phase:

lib.hpp:

template<class T> T f(T v);

lib.cpp:

#include "lib.hpp"

template<> long f(long v) { return -v; }
template<> bool f(bool v) { return !v; }

main.cpp:

#include <iostream>
#include "lib.hpp"

int main()
{
    std::cout
        << f(123L) << ", "
        << f(true) << ", "
        << f(234) << "\n"
    ;
}

gcc output:

c++ -O2 -pipe -c main.cpp
c++ -O2 -pipe -c lib.cpp
c++ main.o lib.o -o main
main.o(.text+0x94): In function `main':
: undefined reference to `int get<int>(int)'

I'd like to have it fail during compilation of main.cpp. Can I somehow declare only specializations actually implemented?

What are my options? The target is C++03, and I'm mainly interested in gcc-4.x and VC9.

A: 

I don't believe it's possible to do what you want. See these FAQs for more info:

How can I avoid linker errors with my template functions?

How can I avoid linker errors with my template classes?

mlimber
the parashift FAQ is at times amusing, but very rarely enlightening (example: *If that's confusing, click your heels twice, think of Kansas, and repeat after me, "I will do it anyway even though it's confusing."*). the two links restate my situation and point me somewhere i don't want to go.
just somebody
A: 

When compiling main.cpp, there's no way for the compiler to know which template specializations may exist in some other compilation unit -- so there's no way to flag this error at compile time, you have to wait until link time.

Martin B
that's why I asked if there was a way to declare only the specializations. the compiler doesn't protest during compilation of *main.cpp* because *lib.hpp* declares `template<class T> f(T)`. i was looking for a way to remove the generic declaration and leave (ok, put) only declarations of the specializations in place. the static assert suggested by others achieves the early error. looks like i don't have to wait for linker after all.
just somebody
Yes, if you're prepared to put your specializations in the header file, you don't have to wait for the linker. If, as originally proposed, you want to put them in a separate compilation unit, then you'll have to rely on the linker.
Martin B
hence my question. again: it compiles because I declared the generic `template<class T> T f(T)`. this declaration makes `X x; f(x);` valid for any `X`. if there was a way to remove this generic declaration and only expose the desired specializations (defined and instantiated in *lib.cpp*), the compiler would bitch about an undeclared function. the static assert trick is very close to that, the only difference is that ideally the base wouldn't be visible at all, while in real world it needs to be there.
just somebody
+5  A: 

It seems to produce a linker error even if you don't put it in the separate file.

However, to produce a compiler error for other instantiations, implement the function and use a compile-time assertion, e.g

#include <boost/static_assert.hpp>

template <class T> T f(T)
{
    //assert some type-dependent "always-false" condition,
    //so it won't be triggered unless this function is instantiated
    BOOST_STATIC_ASSERT(sizeof(T) == 0 && "Only long or bool are available");
}

template<> long f(long v) { return -v; }
template<> bool f(bool v) { return !v; }

int main()
{
    //f(100);
    f(100L);
    f(false);
}

And just for general information, C++0x has a much more elegant way to deal with it:

template <class T> T f(T) = delete;

template<> long f(long v) { return -v; }
template<> bool f(bool v) { return !v; }
UncleBens
The static assertion will not fail for empty types, such as struct Empty{}
Emile Cormier
@Emile: Empty types have a size of 1.
GMan
@GMan: Thanks. I learn something new every day! :-)
Emile Cormier
just what I need. rationale for the "type-dependent" comment in your code: "Normally static assertions when used inside a class or function template, will not be instantiated until the template in which it is used is instantiated. However, there is one potential problem to watch out for: if the static assertion is not dependent upon one or more template parameters, then the compiler is permitted to evaluate the static assertion at the point it is first seen, irrespective of whether the template is ever instantiated" (http://www.boost.org/doc/libs/1_41_0/doc/html/boost_staticassert.html)
just somebody
While that `= delete` is *more* elegant, I'd hesitate to make say the same as a non-relative claim.
BCS