C++模板中的typename vs class:完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

"typename"和"class"背后隐藏着什么?你必须知道的区别

\n\n

如果你曾经用C++编写过模板,那么你一定遇到过两个看似可以互换的关键字:typename和class。这看起来像是一个小细节,但在Stack Overflow上,这个问题已经积累了数千次浏览。为什么?因为在看似简单的背后,隐藏着一个可能破坏你代码编译的陷阱。

\n\n

在本文中,我们将一劳永逸地弄清楚:C++模板中typename和class有什么区别,何时可以将它们用作同义词,以及何时会导致错误。没有废话——只有代码、示例和明确的规则。

\n\n

简短回答:到底有没有区别?

\n\n

Stack Overflow上最流行的回答是:对于声明模板参数来说——没有区别。你可以这样写:

\n\n
template <typename T> void foo(T t);
\n

或者

\n
template <class T> void foo(T t);
\n\n

两种写法完全合法,并且做的是同一件事:声明模板参数T。从历史上看,class首先出现(早在C++98中),而typename后来才添加,以强调参数不仅可以是一个类,还可以是任何类型(int、double、指针等)。

\n\n

然而,魔鬼藏在细节中。有一种场景下,typename是必需的,而class则行不通。这就是——依赖名称(dependent names)。

\n\n

主要陷阱:依赖类型(dependent types)

\n\n

想象一下这种情况:你正在编写一个模板,在其中访问嵌套在模板参数中的类型。例如,你有一个容器,你想获取它的迭代器:

\n\n
template <typename Container>\nvoid printFirst(const Container& c) {\n    Container::const_iterator it = c.begin(); // 错误!\n    std::cout << *it;\n}
\n\n

为什么这不能编译?因为Container::const_iterator是一个依赖名称。编译器在解析阶段不知道Container是什么。它可能是一个类,也可能是一个静态变量的名称?默认情况下,C++认为Container::const_iterator是一个变量(或静态数据成员)的名称,而不是类型。只有关键字typename告诉编译器:"嘿,这是一个类型!"。

\n\n

修正后:

\n\n
template <typename Container>\nvoid printFirst(const Container& c) {\n    typename Container::const_iterator it = c.begin(); // 正确!\n    std::cout << *it;\n}
\n\n

重要提示:这里class行不通。写class Container::const_iterator是语法错误。typename是唯一正确的选择。

\n\n

何时使用typename而不是class?

\n\n

记住一个简单的规则:typename用于两种情况:

\n
    \n
  • 作为模板参数(与class同义):template <typename T>
  • \n
  • 用于指示依赖名称是一个类型:typename T::value_type
  • \n
\n\n

而class仅用于第一种情况。而且,许多C++程序员更倾向于在参数中使用typename,以避免与类声明混淆。

\n\n

带解释的完整示例

\n\n

让我们分析一个实际代码,它演示了C++模板中typename和class的区别:

\n\n
#include <iostream>\n#include <vector>\n#include <list>\n\n// 步骤1:使用class的模板(或typename——没有区别)\ntemplate <class T>\nclass MyContainer {\npublic:\n    // 步骤2:对依赖类型使用typename\n    // T::value_type 是一个类型,而不是                

博客

书籍推荐