C++中 using 命名别名和命名别名模板的用法
1. 格式说明
C++ 11 标准之后开始有这种用法。
- 类型别名是指代先前定义的类型的名称(类似于 typedef )。
- 别名模板是指代类型族的名称。
声明的语法格式:
using identifier attr (optional) = type-id ; | (1) | ||||||||
template < template-parameter-list >
| (2) | ||||||||
template < template-parameter-list > requires constraint
| (3) | (since C++20) | |||||||
当然,上述两行的都可以写在一行中,不过为了美观模板参数和 using 句子分两行。
attr | - | 任意数量的可选属性序列。 |
identifier | - | 根据这个声明引入的名称, 其要么为一个类型名 (1) 要么是一个模板名 (2)。 |
template-parameter-list | - | 模板参数列表, 同模板声明的参数列表。 |
constraint | - | 一个约束表达式,其限制这个别名模板接受的模板参数。 |
type-id | - | 抽象声明符或任意其它有效的类型标识符(其可以引入一个新的类型,如在 type-id 中指出的那样),类型标识符不能直接或间接地引用修饰符,注意,修饰符的声明点位于 type-id 后面的分号处。 |
解释:
(1) 类型别名声明引入一个名称,该名称可用作 type-id 所表示类型的同义词。它不会引入新类型,也不能改变现有类型名称的含义。类型别名声明与 typedef 声明之间没有区别。此声明可以出现在块作用域、类作用域或命名空间作用域中。
(2) 别名模板是一种模板,其特化后等同于用别名模板的模板实参替换 type-id 中的模板形参。
template<class T>
struct Alloc {};
template<class T>
using Vec = vector<T, Alloc<T>>; // type-id 是 vector<T, Alloc<T>>
Vec<int> v; // Vec<int> 与 vector<int, Alloc<int>> 相同
(3) 当特化别名模板的结果是一个依赖模板 ID 时,后续替换将应用于该模板 ID:
template<typename...>
using void_t = void;
template<typename T>
void_t<typename T::foo> f();
f<int>(); // 错误, int 没有嵌入类型 foo
( 注:template<typename...> 表示可变模板参数 )
(4) 特化别名模板时生成的类型不允许直接或间接使用其自身的类型:
template<class T>
struct A;
template<class T>
using B = typename A<T>::U; // type-id 是 A<T>::U
template<class T>
struct A { typedef B<T> U; };
B<short> b; // 错: B<short> 通过 A<short>::U 使用了其自身的类型
(5) 在推导模板模板参数时,别名模板永远不会通过模板实参推导来推导。不可能部分或显式地特化别名模板。
(6)与任何模板声明一样,别名模板只能在类作用域或命名空间作用域内声明。
(7) 别名模板声明中出现的 lambda 表达式的类型在该模板的实例之间是不同的,即使 lambda 表达式不依赖。
template<class T>
using A = decltype([] {}); // A<int> 和 A<char> 指不同的闭包类型
2. 应用举例:
#include <iostream>
#include <string>
#include <type_traits>
#include <typeinfo>
// 类型别名, 等价于
// typedef std::ios_base::fmtflags flags;
using flags = std::ios_base::fmtflags;
// 名称 'flags' 现在表示一个类型:
flags fl = std::ios_base::dec;
// 类型别名, 等价于
// typedef void (*func)(int, int);
using func = void (*) (int, int);
//名称 'func' 现在表示一个函数指针:
void example(int, int) {}
func f = example;
// 别名模板
template<class T>
using ptr = T*;
// 现在 'ptr<T>' 表示一个T指针的别名
ptr<int> x;
// 类型别名用于隐藏一个模板参数
template<class CharT>
using mystring = std::basic_string<CharT, std::char_traits<CharT>>;
mystring<char> str;
// 类型别名可以引入成员typedef 名
template<typename T>
struct Container { using value_type = T; };
// 可用于泛型编程
template<typename ContainerT>
void info(const ContainerT& c)
{
typename ContainerT::value_type T;
std::cout << "ContainerT is `" << typeid(decltype(c)).name() << "`\n"
"value_type is `" << typeid(T).name() << "`\n";
}
// 类型别名用于简化 std::enable_if 的语法
template<typename T>
using Invoke = typename T::type;
template<typename Condition>
using EnableIf = Invoke<std::enable_if<Condition::value>>;
template<typename T, typename = EnableIf<std::is_polymorphic<T>>>
int fpoly_only(T) { return 1; }
struct S { virtual ~S() {} };
int main()
{
Container<int> c;
info(c); // Container::value_type 在此函数中将为 int
// fpoly_only(c); // error: enable_if prohibits this
S s;
fpoly_only(s); // okay: enable_if 允许这样做
}
可能输出:
ContainerT is `struct Container<int>` value_type is `int`