typename在C语言中到底该怎么用?

99ANYc3cd6
预计阅读时长 17 分钟
位置: 首页 C语言 正文

这是一个非常容易混淆的概念,因为 typename 并不是 C 语言本身的关键字,而是 C++ 语言引入的一个关键字,它在 C 语言中的出现通常与宏定义有关,用来模拟 C++ 的某些特性。

c语言typename
(图片来源网络,侵删)

下面我将分两部分来解释:

  1. C++ 中的 typename:这是它的“正宗”用法。
  2. C 语言中的 typename:解释为什么在纯 C 代码中你可能会看到它,以及它意味着什么。

第一部分:C++ 中的 typename(正宗用法)

在 C++ 中,typename 是一个关键字,它的作用是告诉编译器,一个依赖类型名(dependent name)是一个类型

听起来很抽象,我们通过一个例子来理解。

什么是“依赖类型名”(Dependent Name)?

当模板的代码依赖于一个模板参数时,这个名称就是“依赖”的。

c语言typename
(图片来源网络,侵删)

在下面的模板中:

template <typename T>
void print_size(const T& container) {
    // container::size_type 是一个依赖类型名
    // 因为 'T' 是模板参数,只有在实例化时(T 是 std::vector<int>),
    // 我们才知道 container::size_type 具体是什么。
    typename T::size_type size = container.size(); 
    std::cout << "Size: " << size << std::endl;
}
  • T 是模板参数。
  • T::size_type 的含义完全取决于传入的 TTstd::vector<int>T::size_type std::vector<int>::size_type(通常是 size_t);Tstd::list<std::string>T::size_type std::list<std::string>::size_type
  • 这种依赖于模板参数的类型名称,就叫做依赖类型名

为什么需要 typename

编译器在解析模板代码时,遇到一个像 T::size_type 的名字,它无法确定这到底是什么。

  • 它可能是一个类型T::size_type 是一个类型别名。
  • 它可能是一个静态成员变量T::static_value 是一个整型变量。
  • 它可能是一个静态成员函数T::foo() 是一个函数。

由于存在歧义,C++ 标准规定:默认情况下,编译器会假定一个依赖的名称不是类型

如果你不写 typename,编译器会认为 T::size_type 是一个变量或函数,从而导致编译错误。

c语言typename
(图片来源网络,侵删)
// 错误的写法
template <typename T>
void print_size_bad(const T& container) {
    // 编译器看到 T::size_type,不知道它是个类型。
    // 它会尝试把它当作一个变量来解析,导致语法错误。
    T::size_type size = container.size(); // 编译失败!
    // ...
}

正确的做法是使用 typename

// 正确的写法
template <typename T>
void print_size_good(const T& container) {
    // typename 关键字明确告诉编译器:
    // "T::size_type 是一个类型,请把它当作类型来处理。"
    typename T::size_type size = container.size(); // 编译成功!
    std::cout << "Size: " << size << std::endl;
}

typename 的使用规则

  • 必须使用:当你在模板内部,通过一个依赖类型(如 T)去访问其嵌套的类型(如 T::NestedType)时,typename 是必需的。

  • 不能使用:当访问一个非依赖类型的嵌套类型时,不能使用 typename

    class MyClass {
    public:
        using MyType = int;
    };
    template <typename T>
    void func() {
        MyClass::MyType x; // MyClass 是一个具体类型,不依赖模板参数,所以不能用 typename
        // typename MyClass::MyType y; // 这样写是错误的,会编译失败
    }
  • 作用域typename 关键字的作用域从它出现的位置开始,到它声明的类型名的末尾。


第二部分:C 语言中的 typename

纯 C 语言中,没有模板,因此也就没有“依赖类型名”这个概念。typename 不是 C 语言的关键字,如果你在一个标准的 C 编译器(如 gcc -std=c99)下使用 typename,会得到一个“未定义的标识符”(undefined identifier)的警告或错误。

为什么你可能会在 C 代码中看到 typeof 或者类似 typename 的宏呢?

答案是:C 语言社区(尤其是 Linux 内核开发)使用宏来模拟 C++ 的 typeof 功能,而 typeof 的宏实现有时会用到 __typeof__,这和 typename 的概念有重叠,但目的不同。

模拟 typeof(C++ 的 decltype 的前身)

C++11 引入了 decltype,用于获取表达式的类型,在 C++ 之前,一些 C 编译器(如 GCC)扩展了 typeof 关键字,为了在其他编译器上也能实现类似功能,或者为了代码的可移植性,开发者会用宏来模拟它。

一个典型的 typeof 宏定义如下:

// 这是一个简化的示例,真实的实现更复杂
#define typeof __typeof__
// 使用示例
int a = 10;
typeof(a) b = 20; // b 的类型被推断为 int

这里的 typeof__typeof__ 的作用是获取一个表达式的类型,这与 C++ 的 typename 的目的(声明一个名称是类型)是不同的。

可能遇到的 typename

虽然不常见,但确实存在一些代码库(或者旧的代码)可能定义了 typename 宏,其目的可能是为了方便与 C++ 代码兼容,或者作为一种“类型别名”的声明方式。

在一个支持 C 和 C++ 的项目中,可能会看到这样的宏:

#ifdef __cplusplus
// 在 C++ 中,typename 是关键字,直接使用
#define TYPENAME typename
#else
// 在 C 中,typename 不是关键字,我们可以把它定义为空或者一个无操作符
// 这样在模板代码中,当 C++ 部分使用 typename 时,C 部分可以忽略它
#define TYPENAME
#endif
// 然后在代码中统一使用 TYPENAME
template<typename T>
void my_cplusplus_func(T t) {
    TYPENAME T::some_type var; // 在 C++ 中展开为 "typename T::some_type"
                               // 在 C 中,如果这段代码能编译,会展开为 " T::some_type var;"
}

注意:这种用法非常罕见,而且容易引起混淆,在纯 C 代码中,你应该避免使用 typename,如果你看到了,它几乎可以肯定是一个宏定义

总结与对比

特性 C++ typename C typeof (宏模拟) C 语言本身
身份 关键字 非关键字
目的 声明一个依赖名称是类型 获取一个表达式的类型 无此功能。
使用场景 模板代码中,用于解析依赖类型名。 用于声明变量时,根据已有变量推断类型。 无。
示例 typename T::value_type typeof(a) b; int a;
编译器支持 所有标准 C++ 编译器。 依赖于特定编译器扩展(如 GCC)或自定义宏。 标准 C 不支持。

给 C 语言开发者的建议

  1. 不要使用 typename:在你的纯 C 项目中,请忘记 typename 这个词,它不是标准 C 的一部分。

  2. 警惕宏:如果你在 C 代码中看到 typename,首先检查它是否是一个宏(#define),很可能是为了兼容 C++ 或模拟其他功能而定义的。

  3. 使用 typedef_Generic:在 C 语言中,如果你想创建类型别名,请使用 typedef (C89 及以后) 或 _Generic (C11)。

    // C 风格的类型别名
    typedef unsigned int uint;
    // C11 的泛型选择 (类似 C++ 的模板)
    #define define_variable(name, value) \
        _Generic((value), \
            int: int, \
            float: float, \
            default: int \
        ) name = (value)
    // 使用
    define_variable(my_int, 42);
    define_variable(my_float, 3.14f);

希望这个详细的解释能帮助你彻底理解 typename 在 C 和 C++ 中的区别和用法!

-- 展开阅读全文 --
头像
织梦文章编辑框模板如何自定义样式?
« 上一篇 04-23
c语言language
下一篇 » 04-23

相关文章

取消
微信二维码
支付宝二维码

目录[+]