C++
编译器支持
自由(freestanding)与宿主(hosted)
语言
标准库
标准库头文件
具名要求
特性测试宏 (C++20)
语言支持库
概念库 (C++20)
诊断库
内存管理库
元编程库 (C++11)
通用工具库
容器库
迭代器库
范围库 (C++20)
算法库
字符串库
文本处理库
数值库
日期和时间库
输入/输出库
文件系统库 (C++17)
并发支持库 (C++11)
执行控制库 (C++26)
技术规范
符号索引
外部库
[编辑] C++ 语言
通用主题
预处理器
注释
关键词
转义序列
流程控制
条件执行语句
if
switch
迭代语句(循环)
for
range-for (C++11)
while
do-while
跳转语句
continue - break
goto - return
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
throw 表达式
try 块
catch 处理程序
命名空间
命名空间声明
命名空间别名
类型
基本类型
枚举类型
函数类型
类/结构体类型
联合类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
默认初始化
值初始化
零初始化
复制初始化
直接初始化
聚合初始化
列表初始化 (C++11)
常量初始化
引用初始化
表达式
值类别
求值顺序
运算符
运算符优先级
替代表示
字面量
布尔 - 整型 - 浮点型
字符 - 字符串 - nullptr (C++11)
用户定义 (C++11)
工具
属性 (C++11)
类型
typedef 声明
类型别名声明 (C++11)
类型转换
隐式转换
static_cast
const_cast
显式转换
dynamic_cast
reinterpret_cast
内存分配
new 表达式
delete 表达式
类
类声明
构造函数
this 指针
访问说明符
friend 说明符
类特有的函数属性
虚函数
override 说明符 (C++11)
final 说明符 (C++11)
explicit (C++11)
static
特殊成员函数
默认构造函数
复制构造函数
移动构造函数 (C++11)
复制赋值
移动赋值 (C++11)
析构函数
模板
类模板
函数模板
模板特化
参数包 (C++11)
杂项
内联汇编
C++ 历史
[编辑] 声明
概览
声明语法
声明说明符序列
声明符
冲突声明
说明符
typedef
inline
virtual 函数说明符
explicit 函数说明符
friend
constexpr(C++11)
consteval(C++20)
constinit(C++20)
存储类说明符
翻译单元局部 (C++20)
class/struct
union
enum
decltype(C++11)
auto(C++11)
alignas(C++11)
constvolatile
包索引说明符 (C++26)
细化类型说明符
属性 (C++11)
声明符
引用
指针
数组
块声明
简单声明
→结构化绑定声明 (C++17)
别名声明 (C++11)
命名空间别名定义
using 声明
using 指令
static_assert 声明 (C++11)
asm 声明
不透明枚举声明 (C++11)
其他声明
命名空间定义
函数声明
类模板声明
函数模板声明
显式模板实例化 (C++11)
显式模板特化
链接规范
属性声明 (C++11)
空声明
[编辑]
声明一个命名变量为引用,即一个已存在的对象或函数的别名。
目录
1 语法
2 引用折叠
3 左值引用
4 右值引用
5 转发引用
6 悬空引用
7 类型不可访问引用
8 调用不兼容引用
9 注意
10 缺陷报告
11 外部链接
[编辑] 语法
引用变量声明是任何简单声明,其声明符具有以下形式
& attr (可选) declarator
(1)
&& attr (可选) declarator
(2)
(C++11 起)
1) 左值引用声明符:声明 S& D; 将 D 声明为 decl-specifier-seq S 确定的类型的*左值引用*。
2) 右值引用声明符:声明 S&& D; 将 D 声明为 decl-specifier-seq S 确定的类型的*右值引用*。
声明符
-
任何声明符,除了另一个引用声明符(不存在对引用的引用)
属性
-
(自 C++11 起) 属性列表
引用要求被初始化以引用有效的对象或函数:参见引用初始化。
不能形成类型“对(可能带cv限定的)void 的引用”。
引用类型不能在顶层进行cv限定;声明中没有这种语法,如果将限定符添加到 typedef-name 或 decltype 说明符,(自 C++11 起) 或 类型模板参数,则会被忽略。
引用不是对象;它们不一定占用存储空间,尽管编译器可能会在必要时分配存储空间以实现所需的语义(例如,引用类型的非静态数据成员通常会使类的大小增加存储内存地址所需的量)。
因为引用不是对象,所以没有引用数组,没有指向引用的指针,也没有对引用的引用。
int& a[3]; // error
int&* p; // error
int& &r; // error
引用折叠
允许通过模板或 typedef 中的类型操作形成对引用的引用,在这种情况下,应用*引用折叠*规则:右值引用到右值引用折叠为右值引用,所有其他组合形成左值引用
typedef int& lref;
typedef int&& rref;
int n;
lref& r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref& r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&
(这与当在函数模板中使用 T&& 时,模板参数推导的特殊规则一起,形成了使 std::forward 成为可能。)
(C++11 起)
[编辑] 左值引用
左值引用可用于别名现有对象(可选具有不同的cv限定)
运行此代码
#include
#include
int main()
{
std::string s = "Ex";
std::string& r1 = s;
const std::string& r2 = s;
r1 += "ample"; // modifies s
// r2 += "!"; // error: cannot modify through reference to const
std::cout << r2 << '\n'; // prints s, which now holds "Example"
}
它们也可以用于在函数调用中实现按引用传递语义
运行此代码
#include
#include
void double_string(std::string& s)
{
s += s; // 's' is the same object as main()'s 'str'
}
int main()
{
std::string str = "Test";
double_string(str);
std::cout << str << '\n';
}
当函数的返回类型是左值引用时,函数调用表达式成为一个左值表达式
运行此代码
#include
#include
char& char_number(std::string& s, std::size_t n)
{
return s.at(n); // string::at() returns a reference to char
}
int main()
{
std::string str = "Test";
char_number(str, 1) = 'a'; // the function call is lvalue, can be assigned to
std::cout << str << '\n';
}
右值引用
右值引用可用于延长临时对象的生命周期(注意,指向 const 的左值引用也可以延长临时对象的生命周期,但不能通过它们进行修改)
运行此代码
#include
#include
int main()
{
std::string s1 = "Test";
// std::string&& r1 = s1; // error: can't bind to lvalue
const std::string& r2 = s1 + s1; // okay: lvalue reference to const extends lifetime
// r2 += "Test"; // error: can't modify through reference to const
std::string&& r3 = s1 + s1; // okay: rvalue reference extends lifetime
r3 += "Test"; // okay: can modify through reference to non-const
std::cout << r3 << '\n';
}
更重要的是,当一个函数同时具有右值引用和左值引用重载时,右值引用重载绑定到右值(包括prvalue和xvalue),而左值引用重载绑定到左值。
运行此代码
#include
#include
void f(int& x)
{
std::cout << "lvalue reference overload f(" << x << ")\n";
}
void f(const int& x)
{
std::cout << "lvalue reference to const overload f(" << x << ")\n";
}
void f(int&& x)
{
std::cout << "rvalue reference overload f(" << x << ")\n";
}
int main()
{
int i = 1;
const int ci = 2;
f(i); // calls f(int&)
f(ci); // calls f(const int&)
f(3); // calls f(int&&)
// would call f(const int&) if f(int&&) overload wasn't provided
f(std::move(i)); // calls f(int&&)
// rvalue reference variables are lvalues when used in expressions
int&& x = 1;
f(x); // calls f(int& x)
f(std::move(x)); // calls f(int&& x)
}
这使得移动构造函数、移动赋值运算符以及其他支持移动的函数(例如std::vector::push_back())在合适时能够自动选择。
因为右值引用可以绑定到 xvalues,所以它们可以引用非临时对象
int i2 = 42;
int&& rri = std::move(i2); // binds directly to i2
这使得可以从不再需要的范围内对象中移动
std::vector
std::vector
assert(v.empty());
转发引用
转发引用是一种特殊的引用,它保留函数参数的值类别,使其能够通过std::forward进行*转发*。转发引用可以是
1) 声明为该函数模板的cv非限定类型模板参数的右值引用的函数模板的函数参数
template
int f(T&& x) // x is a forwarding reference
{
return g(std::forward
}
int main()
{
int i;
f(i); // argument is lvalue, calls f
f(0); // argument is rvalue, calls f
}
template
int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified
template
struct A
{
template
A(T&& x, U&& y, int* p); // x is not a forwarding reference: T is not a
// type template parameter of the constructor,
// but y is a forwarding reference
};
2) auto&&,除非从花括号初始化列表推导,或者在类模板参数推导期间表示类模板的模板参数时(自 C++17 起)
auto&& vec = foo(); // foo() may be lvalue or rvalue, vec is a forwarding reference
auto i = std::begin(vec); // works either way
(*i)++; // works either way
g(std::forward
for (auto&& x: f())
{
// x is a forwarding reference; this is a common way to use range for in generic code
}
auto&& z = {1, 2, 3}; // *not* a forwarding reference (special case for initializer lists)
另请参阅模板参数推导和std::forward。
(C++11 起)
[编辑] 悬空引用
尽管引用在初始化时总是引用有效的对象或函数,但可能会创建程序,其中被引用对象的生命周期结束,但引用仍然可访问(*悬空*)。
给定一个引用类型的表达式 expr,并令 target 为该引用所指的对象或函数
如果在 expr 求值上下文中指向 target 的指针是有效的,则结果指代 target。否则,行为未定义。
std::string& f()
{
std::string s = "Example";
return s; // exits the scope of s:
// its destructor is called and its storage deallocated
}
std::string& r = f(); // dangling reference
std::cout << r; // undefined behavior: reads from a dangling reference
std::string s = f(); // undefined behavior: copy-initializes from a dangling reference
请注意,右值引用和指向const的左值引用会延长临时对象的生命周期(有关规则和例外,请参阅引用初始化)。
如果被引用对象已被销毁(例如通过显式析构函数调用),但存储未被释放,则对已超出生命周期对象的引用可以以有限的方式使用,并且如果对象在相同的存储中重新创建,则可能变得有效(有关详细信息,请参阅超出生命周期的访问)。
[编辑] 类型不可访问引用
尝试将引用绑定到对象,其中转换后的初始化器是左值(直到 C++11)glvalue(自 C++11 起),通过该glvalue对象不是类型可访问的,会导致未定义行为
char x alignas(int);
int& ir = *reinterpret_cast
// initializer refers to char object
[编辑] 调用不兼容引用
尝试将引用绑定到一个函数,其中转换后的初始化器是一个左值(直到 C++11)一个glvalue(自 C++11 起),其类型与函数定义的类型不兼容,会导致未定义行为
void f(int);
using F = void(float);
F& ir = *reinterpret_cast
// initializer refers to void(int) function
[编辑] 注意
功能测试宏
值
标准
特性
__cpp_rvalue_references
200610L
(C++11)
右值引用
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告
应用于
发布时的行为
正确的行为
CWG 453
C++98
不清楚引用不能绑定到哪个对象或函数
已明确
CWG 1510
C++11
在 decltype 的操作数中无法形成 cv-qualified 引用
允许
CWG 2550
C++98
参数可以有类型“对 void 的引用”
已禁用
CWG 2933
C++98
访问悬空引用的行为不明确
已明确
[编辑] 外部链接
Thomas Becker, 2013 - C++ 右值引用解释