C++ 从入门到精通之十九

大纲

C++ 进阶

decltype 类型推导说明符

核心概念

decltype 是 C++ 11 引入的类型推导说明符,用于在编译期获取实体或表达式的类型,而不对表达式进行求值。它与 auto 的主要区别在于类型推导规则不同:auto 通常按照类似函数模板实参推导的规则确定类型,而 decltype 对未加括号的变量名等特殊表达式采用实体的声明类型,对其他表达式则根据表达式的值类别确定类型,因此能够准确保留引用以及 const、volatile 等类型限定符。decltype 常用于泛型编程、尾置返回类型、类型萃取以及 SFINAE 等场景,是编写通用模板代码的重要工具。

适用场景

  • decltype 的适用场景

    • 泛型编程中推导表达式类型:当表达式的类型依赖模板参数、难以手动指定时,可以使用 decltype 获取表达式的类型。
    • 尾置返回类型:当函数的返回类型依赖参数表达式时,可以使用 decltype 推导返回类型。
    • 完美转发:配合万能引用(转发引用)和 std::forward,根据模板参数的推导结果保留实参的值类别(左值 / 右值)。
    • 保留 cv 限定符与引用:需要保留 const、volatile 及引用类型时,可以使用 decltype 精确获取实体或表达式的类型。
    • 声明与某实体或表达式具有相同类型的变量:例如 decltype(x) y = ...;,使 y 的类型按照 decltype(x) 的规则确定。
    • 萃取函数返回类型:使用 decltype(f()) 获取函数调用表达式的类型,而不会实际调用函数。
    • 类型萃取与模板元编程:使用 decltype 获取表达式的类型,并将其用于类型别名、模板参数及其他类型计算。
    • SFINAE 与类型检测:结合模板替换规则,利用 decltype 检测表达式是否合法,例如检测某个类型是否支持指定的成员操作。
  • decltype 的不适用场景

    • 需要获取表达式的运行结果:decltype 只用于编译期类型分析,不会对表达式进行求值,因此不能获取表达式的实际运行结果。
    • 希望采用 auto 的常规类型推导规则:如果希望通常忽略顶层 const 和引用,应考虑使用 auto;但 auto 的具体推导行为仍取决于声明形式。
    • 直接获取花括号初始化列表的类型:decltype({1, 2, 3}) 不合法,因为花括号初始化列表不是表达式;应根据需求使用 auto 或显式指定类型。
    • 直接获取函数指针类型:对于函数名 f,decltype(f) 得到函数类型而不是函数指针类型;需要函数指针时,可以使用 decltype(f)*。
    • 希望自动进行数组到指针退化:decltype(arr) 会保留数组类型,例如 int[3],不会自动退化为 int*;需要时应显式进行相应的类型转换或声明。
    • 进行运行时类型判断:decltype 用于编译期类型推导,不能获取运行时才确定的对象类型;需要运行时类型识别时,可根据场景考虑 dynamic_cast 或 typeid。
    • 使用未声明或不可访问的实体:相关名字必须满足名称查找、作用域及访问控制等规则,否则可能导致编译失败。
    • 直接替代类型别名声明:decltype 本身不能替代 using 或 typedef 的命名作用,但可以配合它们定义类型别名。

推导规则

特别注意

以下案例代码都运行在 Windows 环境,并依赖了 Boost 库(主要是为了方便查看 auto 类型推导的结果,使用的 Boost 库版本是 1.92)。

作用于变量名
推导规则
  • decltype 作用于变量名的推导规则:

    • 对未加括号的变量名使用 decltype 是一种特殊情况,直接返回该变量的声明类型,完整保留 const、volatile 及引用类型等信息。
    • 与 auto 的关键区别:
      • const int a = 10; auto x = a; 中,auto 按常规推导规则忽略顶层 const,推导结果为 int。
      • const int a = 10; decltype(a) d = 0; 中,decltype(a) 保留变量 a 的声明类型,推导结果为 const int。
    • 变量名加上括号的区别:
      • decltype(x) → int(未加括号的变量名,直接返回声明类型)。
      • decltype((x)) → int&(加括号后的变量名属于普通表达式,x 是左值,因此推导为左值引用)。
      • 注意:有名字的右值引用变量本身是左值表达式,因此 decltype((rr)) 的结果是左值引用类型 int&,而不是右值引用类型 int&&。
  • decltype 作用于变量名的常见案例:

    变量声明decltype 使用示例推导结果说明
    int x;decltype(x)int未加括号的变量名,返回声明类型
    const int a = 0;decltype(a)const int保留顶层 const
    volatile int v;decltype(v)volatile int保留顶层 volatile
    const volatile int cv = 0;decltype(cv)const volatile int同时保留 const 和 volatile
    int& r = x;decltype(r)int&保留左值引用类型
    const int& b = a;decltype(b)const int&保留引用和 const 限定符
    int&& rr = 0;decltype(rr)int&&保留右值引用类型
    int arr[3];decltype(arr)int[3]保留数组类型,不退化为指针
    int* p;decltype(p)int*返回指针类型
    MyClass obj;decltype(obj)MyClass返回类对象的声明类型
    int x;decltype((x))int&加括号后,x 是左值表达式,推导为左值引用
    const int a = 0;decltype((a))const int&加括号后,保留 const 并推导为左值引用
    int&& rr = 0;decltype((rr))int&有名字的右值引用变量是左值表达式,因此推导为左值引用
推导案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <boost/type_index.hpp>
#include <iostream>

// 自定义类
class MyClass {
public:
float i;
double j;
};

// 辅助打印推导类型(保留 const/volatile/引用 属性)
template <typename T>
void print_type(const char* name) {
std::cout << name << " = " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;
}

int main() {
int x = 0;
int y = 0;
const int a = 1;
const int& b = a;
MyClass my_class;

// 案例 1:auto 值传递,顶层 const 和引用均被丢弃
auto c = a; // auto = int,c 类型为 int
print_type<decltype(c)>("decltype(c)");

// 案例 2:decltype 作用于变量名
decltype(a) d = 15; // decltype(a) = const int,d 类型为 const int
print_type<decltype(d)>("decltype(d)");

// 案例 3:decltype 作用于引用变量名
decltype(b) e = d; // decltype(b) = const int&,e 类型为 const int&
print_type<decltype(e)>("decltype(e)");

// 案例 4:decltype 作用于类类型对象
decltype(my_class) f; // decltype(my_class) = MyClass,f 类型为 MyClass
print_type<decltype(f)>("decltype(f)");

// 案例 5:decltype 作用于类数据成员
decltype(MyClass::i) g = 5.0; // decltype(MyClass::i) = float,g 类型为 float
print_type<decltype(g)>("decltype(g)");

// 案例 6:auto&& 作为万能引用绑定左值
auto&& h = x; // auto = int&,int& && 经引用折叠变为 int&,h 类型为 int&
print_type<decltype(h)>("decltype(h)");

// 案例 7
decltype(h)&& i = y; // decltype(h) = int&,int& && 经引用折叠变为 int&,i 类型为 int&
print_type<decltype(i)>("decltype(i)");

// 案例 8:auto 推导数组——数组退化为指针
int arr[3] = {1, 2, 3};
auto k = arr; // arr 退化为指向首元素的指针,auto = int*,k 类型为 int*
print_type<decltype(k)>("decltype(k)");

// 案例 9:decltype 作用于数组名——保留完整数组类型,不退化为指针
decltype(arr) l; // decltype(arr) = int[3],l 类型为 int[3]
print_type<decltype(l)>("decltype(l)");

// 案例 10:decltype 作用于数组下标表达式——返回左值,得到元素引用类型
decltype(arr[0]) m = x; // decltype(arr[0]) = int&,m 类型为 int&
print_type<decltype(m)>("decltype(m)");

// 案例 11:decltype 作用于数组名加上括号——退化为左值表达式,得到数组引用类型
decltype((arr)) n = arr; // decltype((arr)) = int(&)[3],n 类型为 int(&)[3]
print_type<decltype(n)>("decltype(n)");

return 0;
}

程序运行的结果如下:

1
2
3
4
5
6
7
8
9
10
11
decltype(c) = int
decltype(d) = int const
decltype(e) = int const&
decltype(f) = MyClass
decltype(g) = float
decltype(h) = int&
decltype(i) = int&
decltype(k) = int*
decltype(l) = int [3]
decltype(m) = int&
decltype(n) = int (&) [3]
作用于表达式
推导规则
  • decltype 作用于表达式的推导规则:

    • 对于不属于未加括号的变量名等特殊情况的表达式,decltype 根据表达式的值类别进行推导:
      • (1) 若表达式是纯右值(prvalue),返回表达式的类型本身,不带引用。
      • (2) 若表达式是左值(lvalue),返回 T&。
      • (3) 若表达式是将亡值(xvalue),返回 T&&。
    • decltype 的注意事项:
      • 对未加括号的变量名使用 decltype 是一种特殊情况,直接返回变量的声明类型;而使用 decltype((x)) 时,括号内的 x 是左值表达式,因此推导结果为 int&。这就是 decltype(x) 与 decltype((x)) 结果不同的原因。
      • decltype 不会对表达式进行求值,因此不会执行表达式中的运行时操作。例如,decltype(x++) 不会实际执行自增,decltype(x = 5) 不会实际执行赋值,decltype(*p) 也不会实际执行指针解引用。但这些表达式仍然必须满足相应的语法和语义要求,才能通过编译。
  • decltype 作用于表达式的常见案例:

    • 注意,在下面的表格内容中:x、y 和 p 假设分别为 int 类型的变量、int 类型的变量和 int* 类型的指针;f() 假设返回非引用类型。
    • 涉及 ++、--、赋值等操作时,以下结论针对内置运算符,重载运算符的行为可能不同。
      表达式类别decltype 使用示例推导结果说明
      左值decltype((x))int&x 是左值表达式,因此推导为左值引用
      左值decltype(x = 5)int&内置赋值表达式的结果是左值
      左值decltype(++x)int&内置前置自增表达式的结果是左值
      左值decltype(*p)int&当 p 是 int* 时,内置指针解引用表达式的结果是左值
      左值decltype(true ? x : x)int&当 x 是 int 左值时,条件表达式的结果也是左值
      左值decltype((void)x, x)int&逗号表达式的结果具有最后一个操作数的值类别和类型;这里最后一个操作数 x 是左值
      纯右值decltype(x++)int内置后置自增表达式的结果是纯右值
      纯右值decltype(x + y)int当 x、y 为 int 时,内置加法表达式的结果是纯右值
      纯右值decltype(f())函数返回类型当 f() 的返回类型为非引用类型时,结果为该返回类型
      将亡值decltype(std::move(x))int&&当 x 是 int 类型的左值时,std::move(x) 是类型为 int&& 的将亡值表达式
      将亡值decltype(static_cast<int&&>(x))int&&将 x 转换为 int&& 后,表达式是将亡值
推导案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <boost/type_index.hpp>
#include <iostream>

// 辅助打印推导类型(保留 const/volatile/引用 属性)
template <typename T>
void print_type(const char* name) {
std::cout << name << " = " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;
}

int main() {
int x = 0;
int& r = x;
const int c = 1;

// 案例 1:decltype 作用于字面量(纯右值)——返回其类型本身,非引用
decltype(5) a = 0; // decltype(5) = int,a 类型为 int
print_type<decltype(a)>("decltype(a)");

// 案例 2:decltype 作用于左值表达式(带括号的变量),得到引用类型
decltype((x)) b = x; // 变量名 x 单独用时是 int,但 (x) 是左值表达式,decltype((x)) 返回 int&,b 类型为 int&
print_type<decltype(b)>("decltype(b)");

// 案例 3:decltype 作用于赋值表达式——赋值返回左值,得到引用类型
decltype(x = 5) d = x; // decltype(x = 5) = int&,d 类型为 int&
print_type<decltype(d)>("decltype(d)");

// 案例 4:decltype 作用于前置自增表达式——返回左值,得到引用类型
decltype(++x) e = x; // decltype(++x) = int&,e 类型为 int&
print_type<decltype(e)>("decltype(e)");

// 案例 5:decltype 作用于后置自增表达式——返回纯右值,得到非引用类型
decltype(x++) f = 0; // decltype(x++) = int,f 类型为 int
print_type<decltype(f)>("decltype(f)");

// 案例 6:decltype 作用于算术表达式——返回纯右值,得到非引用类型
decltype(x + c) g = 0; // decltype(x + c) = int,g 类型为 int
print_type<decltype(g)>("decltype(g)");

// 案例 7:decltype 作用于返回右值引用的函数调用(结果为将亡值)
decltype(std::move(x)) h = 0; // std::move 返回 int&&,h 类型为 int&&
print_type<decltype(h)>("decltype(h)");

// 案例 8:decltype 作用于解引用表达式——返回左值,得到引用类型
int* p = &x;
decltype(*p) i = x; // decltype(*p) = int&,i 类型为 int&
print_type<decltype(i)>("decltype(i)");

// 案例 9:decltype 作用于三元表达式——两个分支均为左值时返回左值
decltype(true ? x : x) j = x; // decltype(true ? x : x) = int&,j 类型为 int&
print_type<decltype(j)>("decltype(j)");

// 案例 10:decltype 作用于逗号表达式——结果为最后一个表达式的类型和值类别
decltype((void)x, x) k = x; // 逗号表达式结果为左值 x,k 类型为 int&
print_type<decltype(k)>("decltype(k)");

return 0;
}

程序运行的结果如下:

1
2
3
4
5
6
7
8
9
10
decltype(a) = int
decltype(b) = int&
decltype(d) = int&
decltype(e) = int&
decltype(f) = int
decltype(g) = int
decltype(h) = int&&
decltype(i) = int&
decltype(j) = int&
decltype(k) = int&
作用域函数名
推导规则
  • decltype 作用于函数名的推导规则:

    • 对未加括号的函数名使用 decltype,返回函数的声明类型本身,而非函数指针,不会发生函数到函数指针的转换。
    • 要点:
      • decltype(f) 与 decltype(&f) 不同:前者得到函数类型 int(),后者得到函数指针类型 int(*)()。
      • 这与 auto 的推导规则不同:auto x = f; 中,函数名 f 会发生函数到函数指针的转换,推导结果为 int(*)()。
      • 函数名作为表达式时属于左值,但 decltype 对未加括号的函数名采用特殊规则,直接返回函数的声明类型,不附加引用。
      • 对函数名加上括号后,decltype((f)) 按普通表达式规则处理。由于 (f) 是左值表达式,因此推导结果为函数左值引用类型 int(&)()。
      • 特别注意,decltype 只进行编译期类型分析,不会实际调用函数。
  • decltype 作用于函数名的常见案例:

    函数声明decltype 使用示例推导结果说明
    int f();decltype(f)int()返回函数类型本身
    int& f();decltype(f)int&()返回类型为 int& 的函数类型
    void f(int);decltype(f)void(int)函数类型包含返回类型和参数类型
    int f();decltype(&f)int(*)()取函数地址,得到函数指针类型
    int f();decltype(f)*int(*)()在函数类型后添加指针,得到函数指针类型
    int f();decltype(f)&int(&)()在函数类型后添加左值引用,得到函数左值引用类型
    int f();decltype((f))int(&)()加括号后按左值表达式规则推导,得到函数左值引用类型
推导案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <boost/type_index.hpp>
#include <functional>
#include <iostream>

int f() {
return 0;
}

int& g() {
static int v = 0;
return v;
}

void h(int) {
}

// 辅助打印推导类型(保留 const/volatile/引用 属性)
template <typename T>
void print_type(const char* name) {
std::cout << name << " = " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;
}

int main() {
// 案例 1:decltype 作用于返回 int 的函数名——返回函数类型本身
decltype(f) a; // decltype(f) = int(),a 类型为 int()
print_type<decltype(a)>("decltype(a)");

// 案例 2:decltype 作用于返回 int& 的函数名——保留返回引用
decltype(g) b; // decltype(g) = int&(),b 类型为 int&()
print_type<decltype(b)>("decltype(b)");

// 案例 3:decltype 作用于带参数的函数名——参数列表也是函数类型的一部分
decltype(h) c; // decltype(h) = void(int),c 类型为 void(int)
print_type<decltype(c)>("decltype(c)");

// 案例 4:decltype 作用于函数名后显式加 *——得到函数指针类型
decltype(f)* d = f; // decltype(f)* = int(*)(), d 类型为 int(*)()
print_type<decltype(d)>("decltype(d)");

// 案例 5:decltype 作用于函数名后显式加 &——得到函数引用类型
decltype(f)& e = f; // decltype(f)& = int(&)(), e 类型为 int(&)()
print_type<decltype(e)>("decltype(e)");

// 案例 6:decltype 作用于加括号的函数名——退化为左值表达式,返回函数引用
decltype((f)) i = f; // decltype((f)) = int(&)(), i 类型为 int(&)()
print_type<decltype(i)>("decltype(i)");

// 案例 7:decltype 结合 std::function 使用
std::function<decltype(f)> j = f; // decltype(f) = int(),作为模板参数传入 std::function,得到 std::function<int()>,j 类型为 std::function<int()>,可用于包装函数 f
print_type<decltype(j)>("decltype(j)");

return 0;
}

程序运行的结果如下:

1
2
3
4
5
6
7
decltype(a) = int ()
decltype(b) = int& ()
decltype(c) = void (int)
decltype(d) = int (*)()
decltype(e) = int (&)()
decltype(i) = int (&)()
decltype(j) = std::function<int ()>

使用案例

使用案例一
  • decltype 应付模板编程中可变类型

代码写法一,不使用 decltype,编译成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>

template <typename T>
class MyClass {
public:
// 定义一个类型为 T::iterator 的迭代器变量 iter
typename T::iterator iter;

void get_begin(T& tmp) {
iter = tmp.begin();
}
};

int main() {
// 定义类型别名 container_type,表示元素类型为 int 的 std::vector 容器类型
using container_type = std::vector<int>;

container_type arr = {1, 2, 3, 4, 5};
MyClass<container_type> mc;
mc.get_begin(arr);
return 0;
}

代码写法二,不使用 decltype,编译失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>

template <typename T>
class MyClass {
public:
// 定义一个类型为 T::iterator 的迭代器变量 iter,iterator 是非常量迭代器,不能直接用于遍历常量容器
typename T::iterator iter;

void get_begin(T& tmp) {
iter = tmp.begin(); // 当 T 为常量容器类型时,tmp.begin() 返回 const_iterator,与 iter 的 iterator 类型不匹配,导致编译失败
}
};

int main() {
// 定义类型别名 container_type,表示元素类型为 int 的 const std::vector 容器类型(常量容器类型)
using container_type = const std::vector<int>;

container_type arr = {1, 2, 3, 4, 5};
MyClass<container_type> mc;
mc.get_begin(arr);
return 0;
}

代码写法三,使用 decltype,解决编译失败的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <vector>

template <typename T>
class MyClass {
public:
// 定义迭代器变量 iter,使用 decltype 推导 T 的 begin() 函数返回值类型,并将其作为迭代器的类型
// decltype(T().begin()) 仅用于编译期类型推导,不会实际创建 T 的临时对象,也不会调用 begin() 函数
// 这里的 T().begin() 是用于描述表达式类型的语法形式,而不是实际执行的函数调用
decltype(T().begin()) iter;

void get_begin(T& tmp) {
iter = tmp.begin();
}
};

int main() {
// 定义类型别名 container_type,表示元素类型为 int 的 const std::vector 容器类型(常量容器类型)
using container_type = const std::vector<int>;

container_type arr = {1, 2, 3, 4, 5};
MyClass<container_type> mc;
mc.get_begin(arr);
return 0;
}
使用案例二
  • decltype 通过变量表达式抽取变量类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <vector>

int main() {
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);

std::vector<int>::size_type size = vec.size();
std::cout << "size = " << size << std::endl;

// 案例 1
// 使用 decltype(vec) 获取变量 vec 的类型 std::vector<int>,再通过 ::size_type 获取其成员类型
// 等价于 std::vector<int>::size_type size2 = size;
decltype(vec)::size_type size2 = size;
std::cout << "size2 = " << size2 << std::endl;

// 案例 2
typedef decltype(sizeof(0)) m_size_t; // 使用 decltype 推导 sizeof(0) 的结果类型,并将其定义为 m_size_t 类型别名
typedef decltype(sizeof(int)) m_size_t; // 使用 decltype 推导 sizeof(int) 的结果类型,并将其定义为 m_size_t 类型别名,与上一行等价
m_size_t num;

return 0;
}

程序运行的结果如下:

1
2
size = 3
size2 = 3
使用案例三

C++ 版本说明

  • C++ 11:支持 auto 类型推导和 decltype 类型推导,但不能将二者组合成 decltype(auto) 来使用。
  • C++ 14 及以后:支持 decltype(auto),可以根据初始化表达式或返回表达式,按照 decltype 的规则推导类型,并保留引用、const 等类型属性。
  • decltype(auto) 用于变量声明
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main() {
int x = 1;
const int& y = x;

auto z = y; // auto 推导为 int,会忽略引用属性并丢弃顶层 const
decltype(auto) z2 = y; // decltype(auto) 遵循 decltype 的推导规则,推导为 const int&,保留 const 和引用属性

return 0;
}

  • decltype(auto) 用于函数返回类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

template <typename T>
decltype(auto) process(T& v) { // 使用 decltype(auto) 根据返回表达式推导返回类型,保留引用等类型属性
v *= 2;
return v; // v 是具名变量表达式,属于左值,因此返回类型为 T&
}

int main() {
int a = 100;
process(a) = 20; // process(a) 返回 int& 时,可以直接修改变量 a;若将上面的 decltype(auto) 改为 auto,则返回类型为 int,函数调用结果是纯右值,不能作为赋值运算符的左操作数,会导致编译失败
std::cout << a << std::endl; // 输出 20

return 0;
}
使用案例四

什么是函数返回类型后置语法

函数返回类型后置语法(Trailing Return Type)是 C++ 11 引入的一种函数声明语法,使用 auto 声明函数,并通过 -> 将返回类型放在参数列表之后。该语法通常与 decltype 配合使用,可以根据参数表达式推导函数的返回类型,尤其适用于模板编程中返回类型依赖于参数类型的场景。使用案例如下:

1
2
3
auto add(int i, int k) -> decltype(i + k) {
return i + k;
}

代码分析

上述代码使用 auto 声明函数 add,通过 -> decltype(i + k) 将返回类型声明在参数列表之后。decltype(i + k) 用于推导表达式 i + k 的类型,由于 i 和 k 均为 int 类型,因此函数的返回类型为 int。上述代码等价于传统的 int add(int i, int k) 写法。

auto 结合 decltype 构成函数返回类型后置语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>

// 重载函数一
int &tf(int &i) {
std::cout << "int &tf(int &i)" << std::endl;
return i;
}

// 重载函数二
double tf(double &d) {
std::cout << "double tf(double &d)" << std::endl;
return d;
}

template <typename T>
auto Func(T &t) -> decltype(tf(t)) { // 使用函数返回类型后置语法,根据 tf(t) 的表达式类型推导 Func 的返回类型
return tf(t); // 调用与参数类型匹配的 tf() 重载函数,并返回结果
}

int main() {
int i = 10;
Func(i); // T 推导为 int,decltype(tf(t)) 推导为 int&

double j = 20.5f;
Func(j); // T 推导为 double,decltype(tf(t)) 推导为 double

return 0;
}

程序运行的结果如下:

1
2
int &tf(int &i)
double tf(double &d)

代码分析

  • 演示了函数返回类型后置语法结合 decltype,根据模板参数及函数重载结果推导函数返回类型的用法。
  • Func() 使用函数返回类型后置语法,通过 decltype(tf(t)) 根据 tf(t) 表达式推导返回类型。
  • 当传入 int 类型的左值时,匹配 int& tf(int& i),因此 Func() 的返回类型为 int&。
  • 当传入 double 类型的左值时,匹配 double tf(double& d),因此 Func() 的返回类型为 double。
  • 注意,这里的 auto 并没有类型推导的语义,它只是函数返回类型后置语法的组成部分。