C++模板类 = 类型名

C++ template class = typename

本文关键字:类型 C++      更新时间:2023-10-16

template <class = typename T::type>是什么意思?你能给我推荐一些描述这个的博客和规范吗?

问题最初来自对 cpp 参考的 sfinae 的解释

template <typename A>
struct B { typedef typename A::type type; };
template <
class T,
class   = typename T::type,      // SFINAE failure if T has no member type
class U = typename B<T>::type    // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);

首先,我将解释typename T::type。这只是成员类型的访问。下面是访问成员类型的示例:

struct foo {
using bar = int;
};
int main() {
foo::bar var{};
// var has type int
}

那么为什么typename呢?它只是意味着我们想要访问一个类型。由于我们在模板中并且T是未知类型,因此foo::bar也可能意味着访问静态变量。为了消除歧义,我们表示我们实际上希望通过显式键入typename来访问类型。

好的,现在class =是什么意思?

class =的意思与typename =相同。在声明模板类型参数时,我们使用classtypename引入:

template<typename A, typename B>
struct baz {};

但与C++中的任何参数一样,名称是可选的。我本可以写这个,以下内容是完全等效的:

template<typename, typename>
struct baz {};

另外,您知道在函数参数中,我们可以指定默认值吗?诸如此类:

void func(int a, int b = 42);
int main () {
func(10); // parameter b value is 42
// We are using default value
}

我们也可以省略参数名称:

void func(int, int = 42);

就像函数参数一样,模板参数可以省略其名称,并且可以具有默认值。

template<typename, typename = float>
struct baz {};
baz<int> b; // second parameter is float

将一切整合在一起

现在我们有这个声明:

template <
class T,
class   = typename T::type,   // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);

在这里,我们声明一个函数,该函数将 int 作为参数,并具有三个模板参数。

拳头参数是一个简单的命名参数。名称T,它是一个类型模板参数。第二个也是类型参数,但它没有名称。但是,它的默认值为T::type,这是T的成员类型。我们通过指定typename来显式告诉编译器T::type必须是T的成员类型。第三个参数与第二个参数类似。

这就是 SFINAE 发挥作用的地方:当使用默认参数时,但作为成员类型不存在T::type,如何为其分配第二个模板参数?我们不能。如果T::type不存在,则无法分配第二个模板参数。但是编译器不会让它出错,而是简单地尝试另一个函数,因为有可能另一个函数是可调用的。

这与简单的重载非常相似。您具有f功能。它需要一个float参数,另一个需要std::string的重载。想象一下,你打电话给f(9.4f).编译器是否因为无法从float构造std::string而阻塞?不!编译器并不愚蠢。它将尝试另一个重载,并将找到float版本并调用它。在SFINAE中可以进行类似的类比。编译器不会停止,因为模板参数中存在一些需要未定义类型的重载。它将尝试另一个重载。