【问题标题】:Variadic template method specialization可变参数模板方法专业化
【发布时间】:2019-06-04 16:45:35
【问题描述】:

这是我目前拥有的代码:

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

【问题讨论】:

  • 你想达到什么目的?
  • “不起作用” 不是有用的问题描述。请在此处提供minimal reproducible example 重现问题(包括逐字错误消息)。
  • int... 是不允许的。 ... 用于扩展模板参数包,int.. 不是模板。 void f(int...x) 不能是函数,因为没有具体的签名。你想用Function 做什么?也许std::initializer_list 可以用来代替包,或者只是std::vector&lt;int&gt;
  • @Quimby 最终结果应该类似于:Function(std::string id, make_pair("key", "value"), make_pair("another_key", "another_value"), ...);
  • @Quimby 无需专业化就可以正常工作。我要做的就是将方法定义移动到单独的源文件中。

标签: c++ templates variadic-templates


【解决方案1】:

您不能将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 禁用所有非 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
    }
}; 

这样做的缺点是非整数值不会自动转换为int

【讨论】:

  • std::vector 和 std::initializer_list 的真正区别是什么?
  • std::vector 可以从初始化列表隐式构造。如果您使用 std::vector 那么它将 1) 构造一个初始化列表,然后 2) 为向量分配内存,然后 3) 将值复制到向量中。仅使用初始化列表就更简洁、更快捷。
【解决方案2】:

有更好的方法来做到这一点。 首先,您似乎想要强制使用相同类型的所有参数(这是由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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 2018-07-31
    • 2023-04-07
    • 1970-01-01
    • 2014-04-19
    相关资源
    最近更新 更多