1

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

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

可能的请求用法(受内部模板类型 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); 
}
4

1 回答 1

3

下面是关于可变参数模板语法、打包和解的一些解释——关于有问题的特定代码以及如何使其工作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...>,我们将从中推断类型Tand Args,以及 (b) 另一个模板参数——由于基本模板,它必须是一个模板参数——我们忽略它内部模板参数,因为我们只需要第一个类型来匹配第二个,而不管内部模板参数如何。

[C] 是检查给定类型是否为 a 的具体用法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>and设置单独的函数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),这是一种常见用法

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

  • 使用 C++20,我们将使用enable_if语法requires替换使用


其他相关的 SO 问题:


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

于 2020-01-14T05:43:48.567 回答