c++c++11templatestemplate-specializationstatic-assert

How to produce compiler error if default template is used?


Using template specialization, I've written a series of functions all having the same name and taking the same argument type, but returning data of the type specified by the template parameter:

template<typename T> T     f          (int x); // Purposefully unimplemented.

template<> inline uint8_t  f<uint8_t> (int x) { return f8  [x]; }
template<> inline uint16_t f<uint16_t>(int x) { return f16 [x]; }
template<> inline uint32_t f<uint32_t>(int x) { return f32 [x]; }
template<> inline uint64_t f<uint64_t>(int x) { return f64 [x]; }

Then I can write code like:

uint32_t a = f<uint32_t>(3);
uint64_t b = f<uint64_t>(7);

I've purposefully left the default template unimplemented to produce a linker error if someone tries to use a version of f for anything other than the specialized types for which I've defined it.

I have two questions:

1) Is there some way I can use static_assert() (or whatever) to produce a compile error (instead of a linker error) if someone tries to use the default template that's more friendly than what I get now: undefined reference to `int f(int)'?

2) Is there some way to do this with templates that maintains the same interface to the programmer, but doesn't require template specialization? (I.e., is there some way to avoid a default template altogether?)


Solution

    1. Is there some way I can use static_assert (or whatever) to produce a compile error (instead of a linker error) if someone tries to use the default template that's more friendly than what I get now: undefined reference to `int f(int)'?

    I think the better solution is the one suggested by Passer By in a comment:

    template<typename T> T f (int x) = delete;
    

    But if you really want to use a static_assert()... I suppose you can try something as follows

    template<typename T>
    T f (int x)
     {
       static_assert( sizeof(T) == std::size_t(-1), "f()!" );
    
       return {};
     }
    
    1. Is there some way to do this with templates that maintains the same interface to the programmer, but doesn't require template specialization? (I.e., is there some way to avoid a default template altogether?)

    Isn't clear to me what do exactly want.

    You don't want specialization and you want avoid default template?

    Supposing that you want only the default template that is available only for a specific set of types, I suppose you can use SFINAE.

    To make an example, the following f() is enabled only if T is an integral type.

    template<typename T>
    typename std::enable_if<std::is_integral<T>{}, T>::type f (int x)
     { return x; }
    

    The following is a full compiling example

    #include <iostream>
    #include <type_traits>
    
    template<typename T>
    typename std::enable_if<std::is_integral<T>{}, T>::type f (int x)
     { return x; }
    
    int main ()
     {
       auto f16 = f<std::uint16_t>(0);
       auto f32 = f<std::uint32_t>(0);
    
       static_assert( std::is_same<decltype(f16), std::uint16_t>{}, "!" );
       static_assert( std::is_same<decltype(f32), std::uint32_t>{}, "!" );
    
       // compilation error
       // auto fd = f<double>(0);
     }