c++ 错误:非命名空间范围中的显式专用化

envsm3lx  于 2022-12-15  发布在  其他
关注(0)|答案(3)|浏览(311)

在下面的类中,我尝试将一个类型擦除函数特化为一个空类型,用于默认初始化的情况,然而,这会产生下面的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) {
gt0wga4j

gt0wga4j1#

你可以改为函数模板重载。

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...);
}

LIVE

xtfmy6hx

xtfmy6hx2#

一种变体可能是完全放弃专门化,而支持模板内的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++20 std::remove_cvref_t<T>等类型限定符,以便constvolatile类型或引用得到相同的处理。
在您的情况下,这将导致:

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

godbolt上演示。

yfwxisqw

yfwxisqw3#

您还可以添加中间阶段:一个“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

相关问题