模板部分规范

template partial specification

提问人:Xin_Wang 提问时间:11/7/2023 更新时间:11/7/2023 访问量:50

问:

我有以下模板类,就像函数包装器一样:

using MethodId = std::uint16_t;
template <typename Func>
class BaseMethod
{
public:
    BaseMethod(const Func &func, std::uint16_t method_id) : _function(func), _method_id(method_id) {}

    template <typename... Args>
    auto operator()(Args... args) -> decltype(_function(std::forward<Args>(args)...))
    {
        return _function(std::forward<Args>(args)...);
    }

    template <typename... Args>
    auto invoke(Args... args) -> decltype(_function(std::forward<Args>(args)...))
    {
        return _function(std::forward<Args>(args)...);
    }

private:
    Func _function;
    MethodId _method_id;
};

使用这个类模板,我可以这样使用:

int adder(int x, int y){return x+y;}
BaseMethod<int(*)(int, int)> foo_tempalte(adder, 0);

但我也想支持这样的用法:

BaseMethod<int, int, int> bar_tempalte(adder, 0);

所以我添加了一个部分规范版本:

template <typename Ret, typename... Args>
class BaseMethod<Ret (*)(Args...)>
{
public:
    BaseMethod(Ret (*func)(Args...), std::uint16_t method_id) : _function(func), _method_id(method_id)
    {
    }

    Ret operator()(Args... args)
    {
        return _function(std::forward<Args>(args)...);
    }

    Ret invoke(Args... args)
    {
        return _function(std::forward<Args>(args)...);
    }

private:
    Ret (*_function)(Args...);
    std::uint16_t _method_id;
};

但是当我打电话时BaseMethod<int, int, int> bar_tempalte(adder, 0);

IDE给了我错误:too many arguments for class template "BaseMethod"

我想知道我的代码有什么问题?谢谢!

C++ 模板 partial-specialization variable-templates

评论

0赞 NathanOliver 11/7/2023
<int, int, int>不匹配。 会调用该专用化,因为它是一个函数指针类型,但只是 3 个类型的参数,与您的任何模板都不匹配。<Ret (*)(Args...)>BaseMethod<int(*)(int, int)>BaseMethod<int, int, int>
2赞 n. m. could be an AI 11/7/2023
专用化不会更改模板的使用方式。 总是有一个论点,无论存在哪些专业或有多少专业都无关紧要。BaseMethod
0赞 Xin_Wang 11/7/2023
感谢您的评论。所以,不可能同时支持和,对吧?BaseMethod<int(*)(int,int)>BaseMethod<int, int, int>
1赞 Jerry Coffin 11/7/2023
@Xin_Wang:可以编写一个支持这两种方法的模板,但你会以完全不同的方式去做。专用化允许您对同一接口进行多个实现。您正在尝试为同一实现提供多个接口。至少在当前的C++中,这似乎完全没有必要。编译器可以从参数中推导出类型,因此您只需使用 ,编译器将从那里处理事情。BaseMethod baz_template(adder, 1);

答:

0赞 Jerry Coffin 11/7/2023 #1

除非你需要支持较旧的编译器(C++17 之前),或者真的,真的,绝对需要强制用户显式传递模板参数,否则我只是让编译器从参数中推断出函数类型:

#include <utility>
#include <cstdint>
#include <iostream>

using MethodId = uint16_t;

template <typename Func>
class BaseMethod
{
public:
    BaseMethod(Func func, MethodId method_id) : _function(func), _method_id(method_id) {}

    template <typename... Args>
    auto operator()(Args... args)
    {
        return _function(std::forward<Args>(args)...);
    }

    // leaving out `invoke` since it's basically the same as operator()

private:
    Func _function;
    MethodId _method_id;
};

int adder(int x, int y){return x+y;}

int add3(int x, int y, int z) { return x + y + z; }

int main() { 
    // your original code still works:
    BaseMethod<int (*)(int, int)> foo_template(adder, 0);
    std::cout << foo_template(5, 6) << "\n";

    // But it's easier to let the compiler deduce the type:
    BaseMethod bar_template(adder, 1); 
    std::cout << bar_template(1, 2) << "\n";

    BaseMethod baz_template(add3, 2);
    std::cout << baz_template(1, 2, 3) << "\n";
}

评论

0赞 Xin_Wang 11/8/2023
明白了!感谢您的认真回复:)