模板模板可变参数包

时间:2020-01-13 07:20:33

标签: c++ variadic-templates template-specialization parameter-pack

有人可以解释下面带有模板参数包的代码。 它是如何工作的?在这种情况下,如何打包和解压缩模板参数:

template<typename Test, template<typename...> class Ref> //#6
struct is_specialization : std::false_type {};

template<template<typename...> class Ref, typename... Args> //#7
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};

可能的要求使用(受Function Template Overloading or Specialization for inner template type std::vector<std::vector<T>>启发)

template <typename T>
bool f(T& x) // #1
{
    std::cout << "body of f\n";
    return f(x);
}

template <typename T>
bool f(std::vector<T>& v) // #2
{
    std::cout << "body of f for vectors\n";
    return true;
}

template<typename T>
typename std::enable_if<
    is_specialization<typename T::value, std::vector>::value, T>::type
bool f(std::vector<T>& v) // #5
{
    std::cout << "body of f for vectors<vectors>\n";
    return true;
}

int main() {
  std::vector<int> v{1,2}
  f(v); 
}

1 个答案:

答案 0 :(得分:3)

以下是有关 variadic模板 语法, 打包 解压缩< / em> -有关所讨论的特定代码以及如何使其生效 1


您似乎想要实现的是区分std::vector<int>std::vector<float>

但是

您的功能#1 过于贪婪,并且会采用所有可能的参数:

template <typename T>
bool f(T& x) // #1
{
    std::cout << "body of f\n";
    return f(x);
}

如果任何调用也适合重载版本之一,这将导致模棱两可。

因此,我们首先需要:

是否在is_vector之间

我们可以使用以下代码实现这一目标:

// [A]
template <class, template <class...> class>
struct is_of_template_type : std::false_type {};

// [B]
template <class T, class... Args, template <class...> class U>
struct is_of_template_type<U<T, Args...>, U> : std::true_type {};

// [C]
template <class Something>
struct is_vector: is_of_template_type<Something, std::vector> {};

[A]是通用案例的基本模板(与继承无关),在进行任何特殊化之前,用于测试给定类型是否为特定模板。该模板参数为:(a)某些类型(b)某些其他类型必须是模板,并且带有一些未知的模板参数。

[B]是true案例的专业化。调用方应提供两个模板参数,但只有第一个模板参数适合作为第二个模板参数给出的模板类型时,它才适合该专业化要求。请注意,该表达式需要两个模板参数:(a)我们将从其推断类型U<T, Args...>T的模板参数Args,以及(b)另一个模板参数-必须由于基本模板而成为模板参数-我们将忽略内部模板参数,因为我们只需要第一个类型与第二个类型匹配即可,而与内部模板参数无关。

[C]是用于检查给定类型是否为vector的特定用法,而无需处理矢量模板参数。


现在我们可以将功能#1 重写为:

template<typename Something>
typename std::enable_if<!is_vector<Something>::value>::type
f(const Something& v) // #1
{
    std::cout << "body of f for generic Something\n";
}

它不像以前那样贪婪,因为它只使用非向量。


现在我们准备好下一个任务:

在不同类型的向量之间进行分隔,即is_vector_of_T

为此,我们将添加以下内容:

template <typename Container, typename T>
struct is_vector_of_T: std::false_type {};

template <typename T>
struct is_vector_of_T<std::vector<T>, T>: std::true_type {};

现在我们可以为std::vector<int>std::vector<float>设置单独的功能:

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, int>::value>::type
f(const Something& v) // #2
{
    std::cout << "body of f for vector<int>\n";
}

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, float>::value>::type
f(const Something& v) // #3
{
    std::cout << "body of f for vector<float>\n";
}

我们可以用它来隔离std::vector<std::vector<int>>吗?

是的,我们可以:

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, std::vector<int>>::value>::type
f(const Something& v) // #4
{
    std::cout << "body of f for vector<vector<int>>\n";
}

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, std::vector<float>>::value>::type
f(const Something& v) // #5
{
    std::cout << "body of f for vector<vector<float>>\n";
}

代码: https://godbolt.org/z/EFeGZk


注意:

  • 在上述所有情况下,我都使用enable_if将方法的返回值声明为void或不存在(SFINAE),这是一种常见用法

    < / li>
  • 我们可以考虑代替重载模板函数来专门化模板类,这可以减少对enable_if

  • 的需求
  • 在C ++ 20中,我们将使用enable_if语法替换requires的使用


其他相关的SO问题:


1 如果可变参数模板打包和打包对于您来说是全新的,我建议您从一些更基本的示例(例如thisthis)开始学习本主题。 该问题与template template parameter特别相关(重复的template不是一个错误),这是一个更高级的主题,您可以按照this作为参考。 然后,问题更具体地与variadic template template parameter有关,与thisthis之类的示例有关。