template partial specification

51 views Asked by At

I hava the following template class like a function wrapper:

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

with this class template, I can use like this:

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

but I also want to support usage like this:

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

So I add a partial specification version:

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

but when I called BaseMethod<int, int, int> bar_tempalte(adder, 0);

the ide gave me error: too many arguments for class template "BaseMethod"

I wonder what is wrong with my code? Thanks!

1

There are 1 answers

1
Jerry Coffin On

Unless you need to support older compilers (pre-C++17) or really, truly, absolutely need to force the user to explicitly pass template arguments, I'd just let the compiler deduce the function type from argument:

#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";
}