可变参数模板方法专业化

Variadic template method specialization

这是我目前拥有的代码:

class Foo
{
public:
    template<typename T, typename... Args>
    void Function(T t1, Args... args){
        // Definition
    }

private:
    template<typename T>
    void Function(T t1){
        // Definition
    }
};

#include "header.h"

int main()
{
    Foo foo;
    foo.Function(1, 2, 3, 4, 5);
    return 0;
}

工作正常。当我尝试将定义分隔为 source.cpp 时,gcc 开始抱怨。我知道我必须专门化模板以分离定义,所以我尝试将下面的代码添加到头文件中:

template<>
void Foo::Function<int, int...>(int t1, int... args);

template<>
void Foo::Function<int>(int);

但没有成功。我错过了什么


编辑:gcc 错误消息:

header.h:15:28: error: expansion pattern ‘int’ contains no argument packs void Foo::Function(int t1, int... args);

header.h:15:48: error: expansion pattern ‘int’ contains no argument packs void Foo::Function(int t1, int... args);

您不能使用 int... 作为参数包,所以这不起作用。此外,要将源代码与定义分开,您必须完全指定模板,因此即使允许该语法,int... 也不起作用。

解决这个问题的方法。

1.使 Function 接受初始化列表。 我们可以编写函数,使其接受 ints:

的初始化列表
#include <initializer_list>

class Foo {
   public:
    void Function(int t1, std::initializer_list<int> t2);
};

void Foo::Function(int t1, std::initializer_list<int> t2) {
    for(int i : t2) {
        // stuff
    }
}

现在,您可以非常轻松地调用 Function,甚至没有模板化:

Foo f; 
f.Function(10, {1, 2, 3, 4, 5});

如果您在其他地方使用模板,您可以将参数包直接扩展到初始化列表中:

template<class... Args>
void invoke_foo(Foo& f, int first, Args... rest) {
    f.Function(first, {rest...}); 
}

2。使用 SFINAE 禁用所有 non-int 重载。 我们可以禁用 Foo::Function 的所有重载,这些重载不仅接受 ints

#include <type_traits>

class Foo {
   public:
    // Requires C++17 for std::conjunction
    // You could write your own std::conjunction too if you'd prefer
    template<class... Args>
    auto Function(int t1, Args... t2)
        -> std::enable_if_t<std::conjunction<std::is_same<Args, int>...>::value>
    {
        // stuff
    }
}; 

这样做的缺点是 non-integral 值不会自动转换为 int

有更好的方法。 首先,您似乎想要强制使用相同类型的所有参数(这是由 std::initializer_list 在接受的答案中完成的)。这可以通过提供额外的显式参数来强制执行:

class Foo
{
public:
    template<typename T, typename... Args>
    void Function(T t1, T t2, Args... args)
    {
        LOG;
        this->Function(t1);
        this->Function(t2, args...);
    }

private:
    template<typename T>
    void Function(T t1)
    {
        LOG << VAR(t1);
    }
};

template<>
void Foo::Function<int>(int x)
{
    LOG << " Spec" << VAR(x);
}

如您所见,如果您为单个参数提供方法的专门化就足够了。

Live demo