在下面的类中,我尝试将类型擦除函数专门化为默认初始化的空类型。然而,这产生了下面的error: explicit specialization in non-namespace scope
。现在我已经做了研究,了解到由于某种原因,gcc不允许在类范围内进行完整的模板专门化,因此我必须将其从类中取出,并将其放在名称空间范围内。然而,这给我留下了更大的痛苦,因为你可以看到类模板形参是用来将参数转发给外部函数的。所以我也要以此为模板。但话又说回来,我不能部分特化函数模板的情况下,Args…是专用于empty_t
的模板。我该如何优雅地解决这个问题?
#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();
}
错误:
<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) {
一种变体可能是完全放弃专门化,而在模板中使用if constexpr
,例如:
if constexpr(std::is_same_v<T, SpecificType>)
{
/* special code */
}
else
{
/* general code */
}
如果需要,您可以删除类型限定符,如std::remove_cv_t<std::remove_reference_t<T>>
或c++ 20std::remove_cvref_t<T>
,以便const
和volatile
类型或引用被同等对待。
在你的例子中,这将导致:
if constexpr(std::is_same_v<T, empty_t>)
{
return myfunc(args...);
}
else
{
return myfunc(args...);
}
godbolt的演示。
你可以改成函数模板重载。例如
template <typename T>
static std::enable_if_t<!std::is_same_v<T, empty_t>, R>
fn_invoke(Args&&... args) {
return myfunc(args...);
}
template <typename T>
static std::enable_if_t<std::is_same_v<T, empty_t>, R>
fn_invoke(Args&&... args) {
return myfunc(args...);
}
生活您还可以添加一个中间阶段:"helper"类模板并在entity
类之外对其进行专门化(如果要使用静态函数或operator(),则调用):
struct empty_t{};
template<typename T, typename R, typename ...Args>
struct detail
{
static R call(Args&&... args)
{
my_func(args...);
}
};
template<typename R, typename ...Args>
struct detail<empty_t, R, Args...>
{
static R call(Args&&... args)
{
my_func2(args...);
}
};
这种方法可以规避部分专门化函数的不可能性。
然后在实体类内部:
template <typename T>
static R fn_invoke(Args&&... args) {
return detail<T, R, Args...>::call(args...);
}
https://godbolt.org/z/ehYYvnofE