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?
#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) {
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.