Skip to the content.

cpp template : using 与 typedef

using 在 C++ 中有三种互不相关的用法,谈 using 和 typedef 的区别时,只有第三种才是同类项:

  1. using-directive: using namespace std;,把整个命名空间引入当前作用域
  2. using-declaration: using std::cout; / using Base::f; / using Base::Base; / using enum Color;,把某个名字引入当前作用域
  3. alias-declaration: using T = int;,类型别名,也就是本文的主角

前两种 typedef 完全没有对应物,详见文末。第三种里,非模板场景两者等价,真正的分水岭是别名模板(alias template),typedef 做不到。

语法形式

typedef int              Int;
typedef int             *IntPtr;
typedef int              Arr[10];
typedef void             (*Func)(int, double);   // 名字埋在声明符中间
typedef void             (Screen::*GetFn)();
typedef std::vector<int> IntVec;

using Int    = int;
using IntPtr = int *;
using Arr    = int[10];
using Func   = void (*)(int, double);            // 名字在最左,右边是完整 type-id
using GetFn  = void (Screen::*)();
using IntVec = std::vector<int>;

区别只在可读性: using 把别名放在最左边,右边可以照抄类型本身的写法;typedef 的名字被塞进声明符里,遇到函数指针、数组、成员指针,基本需要手工拆解。这也是各种规范推荐 using 的首要理由。

非模板场景: 完全等价

对于不涉及模板参数的情形,两者语义完全相同,都是给类型起名字,不创建新类型:

typedef int I;
using J = int;
static_assert(std::is_same<I, J>::value, "");
static_assert(std::is_same<I, int>::value, "");  // 别名是透明的

由此推出的共同性质:

C 兼容场景下 typedef 仍是唯一选择:

typedef struct S { int a; } S;  // C 中 struct tag 和类型名分属两个命名空间
typedef struct T T;             // C++ 中也常这么写

别名模板

typedef 无法带模板参数。想给 std::vector<T> 起个短名字,C++98 只能绕道 class template + 嵌套 ::type:

// C++98 的老办法
template <typename T> struct MyVec { typedef std::vector<T> type; };

template <template <typename> class C> struct X { typename C<int>::type c; };
X<MyVec> x;   // 使用侧还要写 typename ... ::type

using 直接支持:

template <typename T> using MyVec = std::vector<T>;   // 别名模板
MyVec<int> v;

template <typename T> using Ptr = MyVec<T> *;

标准库 _t 后缀的别名家族(C++14 起)就是靠这个把 trait 的 ::type 擦掉的:

// 以前
typename std::enable_if<std::is_integral<T>::value, T>::type
// 现在
std::enable_if_t<std::is_integral<T>::value, T>

std::remove_reference_t<T>   std::conditional_t<B, T, F>   std::void_t<Ts...>

本仓库中的实例:

别名模板和依赖名消歧经常一起出现:

using T1 = typename X<T>::type;                                  // 目标类型是依赖名
using T2 = std::pointer_traits<int *>::template rebind<int *>;   // 依赖名里的模板成员

code/basic/modern/using.cpp 里这两个例子都有。

限制一: 别名模板不可特化

全特化和偏特化都不允许(clang 报 partial specialization of alias templates is not permitted):

template <class T> using Ptr = T *;
template <class T> using Ptr<T *> = T **;   // 错误

要按类型分支,只能回到 class template + ::type,或者用 std::conditional_t 一类的工具组合。

限制二: 作为模板模板参数的实参

历史上把别名模板传给模板模板参数是非法的,CWG 1286 之后被允许,且作为 DR 追溯生效,所以现在 -std=c++11 也放行:

template <typename T> using MyVec = std::vector<T>;
template <template <typename> class C> struct X { C<int> c; };
X<MyVec> x;   // g++ 15.2 / clang++ 21.1 实测通过

别名的透明性

别名不创造新类型,下面这些都不行:

using Meters = double;
using Seconds = double;
// 无法区分两者,也无法为 Meters 单独重载 operator+
// 想要强类型必须写 struct / enum class

也不要把别名当作特化的锚点:

template <class T> struct is_mine : std::false_type {};
template <> struct is_mine<Meters> : std::true_type {};  // 特化的其实是 double

using-declaration

这部分和别名无关,但 using 经常一起出现,容易混淆。typedef 没有任何对应能力:

// 1. 引入单个名字,替代 using namespace
using std::cout;

// 2. 恢复被隐藏的基类 overload
struct Base { void f(int); void f(double); };
struct Derived : Base {
    void f(int) override;   // 只 override 一个,Base::f(double) 被隐藏
    using Base::f;          // 把 Base::f(double) 带回来
};

// 3. 修改基类成员的可见性(protected -> public)
struct D2 : Base { public: using Base::f; };

// 4. 继承构造函数(拷贝、移动、默认构造函数不会被继承)
struct D3 : Base { using Base::Base; };

// 5. 在模板中引入依赖基类的成员
template <class T> struct D4 : Base<T> {
    using typename Base<T>::type;   // 类型成员需要 typename
    using Base<T>::data;            // 非类型成员不需要,见 template/dependent-name-2.cpp
};

// 6. C++20
enum class Color { red, green };
using enum Color;   // 之后可以直接写 red,不用 Color::red

对比表

场景 typedef using
普通类型别名 typedef int I; using I = int;
函数指针 typedef void (*F)(int); using F = void (*)(int);
数组成员 typedef int A[10]; using A = int[10];
成员函数指针 typedef void (C::*M)(); using M = void (C::*)();
别名模板 不支持,需 class template + ::type template <class T> using V = ...;
别名模板特化 不适用 不支持
重复声明同一别名 允许 允许
引入命名空间/基类名字 不支持 using-declaration
继承构造函数 不支持 using Base::Base;
C 兼容 是,唯一选择 否
创建新类型 否 否

风格结论

理由三条: 可读性(名字在前)、别名模板(typedef 做不到)、语义一致。仍需 typedef 的场合基本只剩 C / C++98 兼容、C 头文件,以及团队编码规范强制。

参考:

human

2026-09-12 : 这里对吗?

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。