C++ 中using的完整用法详解
using在 C++ 里是个"多面手",从 C++98 到 C++20 被不断扩展。按用途可以分成以下几类:
| # | 用法 | 引入版本 | 典型场景 |
|---|---|---|---|
| 1 | using 声明(引入单个名字) | C++98 | using std::cout; |
| 2 | using 指令(引入整个命名空间) | C++98 | using namespace std; |
| 3 | 命名空间别名 | C++98 | namespace fs = std::filesystem; |
| 4 | 类型别名(替代 typedef) | C++11 | using IntVec = std::vector<int>; |
| 5 | 模板别名 | C++11 | template<class T> using Vec = std::vector<T>; |
| 6 | 类内 using(解除隐藏 / 改权限) | C++98 | using Base::foo; |
| 7 | 继承构造函数 | C++11 | using Base::Base; |
| 8 | using enum | C++20 | using enum Color; |
下面逐个说明,最后给一个综合小例子。
1. using 声明(using-declaration):把单个名字引入当前作用域
cpp
#include <iostream> void f() { using std::cout; // 只引入 cout 这一个名字 cout << "hello\n"; // OK // cin << ...; // 错误,cin 没被引入 }也可以在命名空间内部引入:
cpp
namespace app { using std::string; // app 里可以直接用 string string name; }特点:只引入指定的名字,不像using namespace那样"污染"整个作用域。在头文件里尤其推荐用这种方式,而不是using namespace。
2. using 指令(using-directive):把整个命名空间的名字都"拉进来"
cpp
#include <iostream> using namespace std; // 整个 std 都可见 int main() { cout << "hi" << endl; }注意:
在头文件里千万不要写
using namespace std;,会让包含它的所有文件都被污染。更好的做法是在函数体内局部使用,或直接用 using 声明。
3. 命名空间别名
cpp
#include <filesystem> namespace fs = std::filesystem; // 给长命名空间起个短名 fs::path p = "/tmp/a.txt";
C++11 起也支持命名空间别名模板(但用得少):
cpp
template<class T> namespace myns = std::chrono; // 实际上不太常见,意义有限
4. 类型别名(type alias declaration)—— 替代typedef
这是using最常用的用法之一。C++11 引入,语法更直观:
cpp
// 老写法 typedef std::vector<int> IntVec; typedef void (*Callback)(int); // 新写法(推荐) using IntVec = std::vector<int>; using Callback = void (*)(int);
为什么推荐using?
using的语法是名字 = 类型,符合直觉;typedef是反的。using可以定义模板别名(见第 5 节),typedef不行。
经典对比——难读的typedef:
cpp
// 一个函数指针,参数是 int,返回 int typedef int (*FuncPtr)(int); // 等价 using,读起来更清楚 using FuncPtr = int (*)(int);
5. 模板别名(alias template)——typedef做不到的
cpp
template<class T> using Vec = std::vector<T, MyAllocator<T>>; Vec<int> v; // 等价 std::vector<int, MyAllocator<int>>
也可以把EmTimer<T>这种冗长类型统一封装(呼应你前面那段代码):
cpp
template<class Tag> using EmTimerAlias = common::platform::EmTimer<Tag>; EmTimerAlias<TempUeTimerInd> t; // 和 EmTimer<TempUeTimerInd> 完全等价
注意:模板别名是真正的别名(同一类型),不像继承那样产生新类型。你之前看到的
struct OptionalEmTimer : std::optional<...>之所以用继承而不用using,正是为了得到一个独立的新类型。
6. 类内 using:解除派生类对基类成员的隐藏 / 改变访问权限
6.1 解决"名字隐藏"
cpp
struct Base { void f(int) { } void f(double) { } }; struct Derived : Base { void f(int) { } // 隐藏了 Base::f(double) }; Derived d; d.f(3.14); // 调用 Derived::f(int)(隐式转换),Base::f(double) 被藏起来了用using把基类的重载集合重新引入:
cpp
struct Derived : Base { using Base::f; // 把 Base 的所有 f 都拉进来 void f(int) { } }; Derived d; d.f(3.14); // 现在正确调用 Base::f(double)6.2 改变访问权限
cpp
class Base { protected: void helper(); }; class Derived : private Base { public: using Base::helper; // 把 protected 的 helper 变成 public };7. 继承构造函数(C++11)
派生类想"原封不动"地用基类的构造函数,不用手写转发:
cpp
struct Base { Base(int) { } Base(int, double) { } }; struct Derived : Base { using Base::Base; // 继承 Base 的所有构造函数 }; Derived d1(1); // 调用 Base::Base(int) Derived d2(1, 2.0); // 调用 Base::Base(int,double)不写using Base::Base时,派生类只会生成默认/拷贝/移动构造,基类的带参构造不会自动继承。
(这正是上一条回复里提到的:OptionalEmTimer那个空 struct 没有using Base::Base;,所以它只能通过默认构造或直接构造为空值,不能用optional(std::in_place, ...)之类的构造。)
8.using enum(C++20)
把枚举的所有枚举项一次性引入当前作用域:
cpp
enum class Color { Red, Green, Blue }; void f() { using enum Color; // 之后可以直接写 Red / Green / Blue Color c = Red; // 不用写 Color::Red }作用域只在函数(或块)内,很安全。等价于逐个写using Color::Red; using Color::Green; ...。
9. 在模板里配合typename使用别名
当using右侧是依赖名(依赖模板参数)时,需要typename:
cpp
template<class Container> using ValueType = typename Container::value_type; ValueType<std::vector<int>> x; // int
这是using与模板、依赖名交互的一个小细节。
#include <iostream> #include <vector> #include <memory> #include <chrono> #include <string> // (3) 命名空间别名:给真实存在的 std::chrono 起短名 namespace ch = std::chrono; namespace demo { using std::string; // (1) using 声明:只引入 string using Name = string; // (4) 类型别名 using Callback = void (*)(Name); // 函数指针别名 template<class T> using Ptr = std::unique_ptr<T>; // (5) 模板别名 struct FastTag {}; struct SlowTag {}; template<class Tag> struct Timer { using TagType = Tag; // (9) 模板内的依赖别名 void run() { std::cout << "timer run\n"; } }; struct Base { Base(int) {} void f(double) { std::cout << "Base::f(double)\n"; } }; struct Derived : Base { using Base::Base; // (7) 继承构造函数 using Base::f; // (6) 解除基类名字隐藏 void f(int) { std::cout << "Derived::f(int)\n"; } }; } // namespace demo int main() { using demo::Name; // (1) using namespace demo; // (2) 块作用域内用,安全 Name n = "alice"; Ptr<int> p = std::make_unique<int>(42); // (5) Timer<FastTag> t; // 与 Timer<FastTag> 同类型 t.run(); Derived d(7); // (7) 继承构造函数 d.f(3.14); // (6) 正确调用 Base::f(double) d.f(7); // 调用 Derived::f(int) ch::milliseconds ms(100); // (3) 命名空间别名演示 std::cout << n << " " << *p << " " << ms.count() << "\n"; return 0; }