如何显式地将某些模板化输入作为参数?

How to explicitly take in certain templated inputs as arguments?

本文关键字:输入 参数 何显式      更新时间:2023-10-16

我有Foo<a, b>Foo<c, d>,其中Foo接受 2 个模板化参数。如果我有一个带有类SomeFunction的可变参数模板,它接受这些Foo输入,如下所示:

template <typename... Ts>
class SomeFunction< /*some input*/ >

我该怎么做才能确保SomeFunction只接受Foo模板,而不是像Blah<a,b>这样的其他模板?

你可以让你的类型私下继承另一个类型,除非类型参数正确,否则无法实例化。 将模板类型保留为未定义,然后部分专用于Foo<...>情况,递归列表的其余部分。

// Base template forward declaration, but note we don't define it.
template <typename...>
struct foo_validator;
// Specialization handling the terminating case.
template <>
struct foo_validator<> {};
// Partial specialization, handling Foo<a, b> followed by anything.  Note in particular
// how it inherits itself with the remainder of the type arguments.
template <typename T1, typename T2, typename ...Tail>
struct foo_validator<Foo<T1, T2>, Tail...> : foo_validator<Tail...> {};
// Now have SomeFunction privately inherit this type.
template <typename ...Ts>
class SomeFunction : private foo_validator<Ts...> {};

如果传递除Foo<A, B>(其中AB可以是任何类型)之外的任何类型参数,则任何专用化都不会匹配,编译器将尝试实例化非专用模板。 由于未定义,这将导致编译错误。

本声明:

SomeFunction<Foo<int, float>, std::pair<short, double>> a;

将失败,并显示:

error: invalid use of incomplete type 'struct foo_validator<std::pair<short int, double> >'


另一种方法非常相似,使用static_assert

// As before, this is the failing case, but we define it to inherit
// std::false_type.
template <typename...>
struct foo_validator : std::false_type {};
// The empty case is success and inherits std::true_type.
template <>
struct foo_validator<> : std::true_type {};
// Again, the "so-far-successful" partial specialization.
template <typename T1, typename T2, typename ...Tail>
struct foo_validator<Foo<T1, T2>, Tail...> : foo_validator<Tail...> {};
// Instead of inheriting foo_validator, we assert on its value member.
template <typename ...Ts>
class SomeFunction {
static_assert(foo_validator<Ts...>::value, "All type arguments must be Foo<,>");
};

现在,SomeFunction的错误实例化将失败,并显示:

error: static assertion failed: All type arguments must be Foo<,>


这是如何工作的? 我们在这两种方法中都有三个声明,它们在两种方法中都扮演相同的角色:

  1. 我们声明一个接受任何类型参数的模板作为失败情况。
  2. 我们将此模板专门用于没有类型参数的情况作为成功案例(空洞的真相)。
  3. 当第一个参数是具有两个类型参数的Foo模板类型的实例化,然后是任意数量(零个或多个)其他类型的参数时,我们声明了部分专用化。 此类型继承验证程序模板类型,并删除第一个类型参数(递归)。

让我们看一个尝试实例化SomeFunction<Foo<int, int>, Foo<float, double>>的示例。

  1. 编译器尝试实例化foo_validator<Foo<int, int>, Foo<float, double>>。这与上面列表中的部分专业化类型3相匹配。(T1 = int, T2 = int, Tail = [Foo<float, double>]) 此类型继承foo_validator<Foo<float, double>>
  2. 编译器尝试实例化foo_validator<Foo<float, double>>。这也与上面列表中的部分专业化类型3相匹配。(T1 = float, T2 = double, Tail = []) 此类型继承foo_validator<>
  3. 编译器尝试实例化foo_validator<>并找到完全匹配的专用化,即上面的列表中键入 2。 在"继承foo_validator"方法中,什么都不会发生,编译继续进行。 在static_assert方法中,此类型继承std::true_type,因此断言成功。

现在让我们看一个不应该起作用的类型:SomeFunction<Foo<int, int>, std::pair<float, double>>

  1. 编译器尝试实例化foo_validator<Foo<int, int>, std::pair<float, double>>。这与上面列表中的部分专业化类型3相匹配。 (T1 = int, T2 = int, Tail = [std::pair<float, double>]) 此类型继承foo_validator<std::pair<float, double>>.
  2. 编译器尝试实例化foo_validator<std::pair<float, double>>。这与上面列表中的类型 2 或 3 不匹配,因此编译器尝试实例化基本模板声明(类型 1)。在"foo_validator方法"中,这失败了,因为模板类型不完整(我们从未定义过它)。在static_assert方法中,基本模板被实例化并继承std::false_type,因此断言失败。