可变参数模板方法专业化

时间:2019-06-04 16:45:35

标签: c++ templates variadic-templates

这是我当前拥有的代码:

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:错误:扩展模式“ int”不包含任何参数   打包void Foo :: Function(int t1,int ... args);

     

header.h:15:48:错误:扩展模式“ int”不包含任何参数   打包void Foo :: Function(int t1,int ... args);

2 个答案:

答案 0 :(得分:2)

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

如何解决这个问题。

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

#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禁用所有非int重载。我们可以禁用Foo::Function的所有重载,这些重载不仅接受int s

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

不利之处在于,非整数值不会自动转换为int

答案 1 :(得分:0)

有更好的方法。 首先,您似乎想强制所有类型的相同参数(这由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