Search code examples
c++templatesmethodstemplate-specialization

error: explicit specialization in non-namespace scope


In the following class I try to specialize a type-erased function to an empty type for the case of default initialization. However, this yields the following error: explicit specialization in non-namespace scope. Now I've done my research and learned that gcc for some reason doesn't allow full template specialization in class scope, so consequently I would have to pull it out of the class and put it in namespace scope. However this leaves me with an even bigger misery bc as you can see the class template parameters are used to forward arguments to an external function. So I would have to templatize on that as well. but then again, I can not partially specialize function templates for the cases that Args... is a template and specialized for empty_t only. How do I resolve this elegantly?

Demo

#include <utility>
#include <cstdio>


template <typename... Ts>
void myfunc(Ts... args)
{
    printf("Do something!\n");
}

template <typename R, typename... Args>
struct entity;

template <typename R, typename... Args>
struct entity<R(Args...)>
{

    using fn_invoke_t = R(*)(void*);

    struct empty_t{};

    template <typename T>
    static R fn_invoke(Args&&... args) {
        return myfunc(args...);
    }

    template <>
    static R fn_invoke<empty_t>(Args&&... args) {
        return myfunc(args...);
    }

    entity()
        :   fn_invoke_(reinterpret_cast<fn_invoke_t>(fn_invoke<empty_t>))
    { }

    template <typename T>
    entity(T obj)
        :   fn_invoke_( reinterpret_cast<fn_invoke_t>(fn_invoke<T>) )
    { }

    fn_invoke_t fn_invoke_;
};

int main()
{
    entity<void()> hello = [](){ printf("Hello World!\n"); };

    // hello();
}

Error:

<source>:27:15: error: explicit specialization in non-namespace scope 'struct entity<R(Args ...)>'
   27 |     template <>
      |               ^
<source>:28:14: error: template-id 'fn_invoke<entity<R(Args ...)>::empty_t>' in declaration of primary template
   28 |     static R fn_invoke<empty_t>(Args&&... args) {

Solution

  • One variant might be dropping the specialisation entirely in favour of an if constexpr inside the template such as:

    if constexpr(std::is_same_v<T, SpecificType>)
    {
        /* special code */
    }
    else
    {
        /* general code */
    }
    

    If need be you can remove type qualifiers like std::remove_cv_t<std::remove_reference_t<T>> or with C++20 std::remove_cvref_t<T> so that const and volatile types or references are treated alike.

    In your case this would result in:

    if constexpr(std::is_same_v<T, empty_t>)
    {
        return myfunc(args...);
    }
    else
    {
        return myfunc(args...);
    }
    

    Demonstation on godbolt.