c++类型推断

为什么要了解类型推断?

C++98

在c++98的年代,只有模板才有类型推断,如果你不使用模板,那么你完全不需要理解类型推断。

C++11

在C++11中,类型推断在模板之外被大量应用。

  • auto
  • lambda captures
  • function return
  • decltype

想要熟练的使用上述新特性,那么就必须要了解类型推断的规则。

C++14

在C++14中,对类型推断又有了进一步的扩展。

  • decltype(auto)
  • lambda init captures

规则

在进行类型推断时,大家始终要记得一点,我们要推断的是两个类型,而非一个!

  • T :这是推断的类型
  • ParamType : 基于T,但是和T经常不一样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <typename T>
void f1(T param) {
}

template <typename T>
void f2(T& param) {
}

template <typename T>
void f4(T* param) {
}

template <typename T>
void f4(T&& param) {
}

C++98

在C++98中,对于模板的类型推断,可以分为两条规则:

  • T
  • T& / T*

C++11

在C++11中,可以分为六条规则:

  • template
    • T
    • T& / T*
    • T&&
  • lambda captures
    • 基于T& / T* 规则,有一些区别
  • lambda return
    • 使用T规则
  • decltype
    • 独立的一套规则
  • auto
    • 基于模板规则,有些细节区别

C++14

在C++14中,新增的扩展使用的还是上述六条规则:

  • decltype(auto)
    • 使用decltype规则
  • lambda init captures
    • 使用auto规则

规则详解

template

T

丢弃CVR(const、volatile、reference),剩下的就是T的类型

当传入的是函数名或者数组名时,T会是函数指针或数组指针

不支持大括号初始化,因为大括号初始化没有类型

因为当param类型为T时,表示param将会是重新定义的一个全新的变量,与传递进来的变量应该彼此独立,所以T不能是引用,引用需要丢弃!一个全新的变量,其CV修饰符应该有自身显示定义,不应该继承其它变量的修饰符,所以CV修饰符应该丢弃。

1
2
3
4
5
6
7
8
template <typename T>
void f1(T param) {
std::cout << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name()
<< "; param = " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}

void test(int) {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void test_f1() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "value" << std::endl;
f1(x); // T = int; param = int
f1(cx); // T = int; param = int
f1(rx); // T = int; param = int
f1(rcx); // T = int; param = int
f1(px); // T = int*; param = int*
f1(pcx); // T = int const*; param = int const*
f1(cpx); // T = int*; param = int*
f1(22); // T = int; param = int

f1(test); // T = void (*)(int); param = void (*)(int)
f1(arr); // T = int*; param = int*
// f1({1, 2, 3}); // Error
}

T&/T*

T&

丢弃引用,剩下的就是T的类型(继承CV)

当传入的是函数名或者数组名时,T会是函数引用或数组引用

因为param的类型是T&,已经有引用了,所以当传进来的变量是引用时,应该忽略引用;param是引用时,赋值是指向外部的变量本身,所以CV描述符不应该丢弃。

1
2
3
4
5
6
7
8
template <typename T>
void f2(T& param) {
std::cout << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name()
<< "; param = " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}

void test(int) {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void test_f2() {
int x = 22;
const int cx = x;
int& rx = x;
int&& rx2 = 22;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "reference" << std::endl;
f2(x); // T = int; param = int&
f2(cx); // T = int const; param = int const&
f2(rx); // T = int; param = int&
f2(rcx); // T = int const; param = int const&
f2(px); // T = int*; param = int*&
f2(pcx); // T = int const*; param = int const*&
f2(cpx); // T = int* const; param = int* const&
f2(rx2); // T = int; param = int&
// f2(22);
f2(test); // T = void (int); param = void (&)(int)
f2(arr); // T = int [3]; param = int (&) [3]
}

const T&

当显示声明CV修饰符时,丢弃CV

1
2
3
4
5
template <typename T>
void f3(const T& param) {
std::cout << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name()
<< "; param = " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void test_f3() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
std::cout << "const reference" << std::endl;
f3(x); // T = int; param = int const&
f3(cx); // T = int; param = int const&
f3(rx); // T = int; param = int const&
f3(rcx); // T = int; param = int const&
f3(px); // T = int*; param = int* const&
f3(pcx); // T = int const*; param = int const* const&
f3(cpx); // T = int*; param = int* const&
f3(22); // T = int*; param = int* const&
}

T*

丢弃指针,丢弃CV,剩下的就是T的类型

因为param的类型是T*,已经有指针了,所以当传进来的变量是指针时,应该忽略指针;param是指针就表明是一个全新的变量,其CV修饰符应该有自身显示定义,不应该继承其它变量的修饰符,所以CV修饰符应该丢弃。

1
2
3
4
5
6
template <typename T>
void f4(T* param) {
std::cout << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name()
<< "; param = " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
void test_f4() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
std::cout << "pointer" << std::endl;
f4(px); // T = int; param = int*
f4(pcx); // T = int const; param = int const*
f4(cpx); // T = int; param = int*
}

T&&

表达式为左值时,T为表达式类型(丢弃引用后)的左值引用;表达式为右值时,T为表达式类型。(继承CV)

注意:param看起来是右值引用,但并不一定是右值引用,只有当表达式为右值时,param才是右值引用

1
2
3
4
5
template <typename T>
void f7(T&& param) {
std::cout << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name()
<< "; param = " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void test_f7() {
int x = 22;
const int cx = x;
int& rx = x;
int&& rx2 = 22;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
std::cout << "universal reference" << std::endl;
f7(x); // T = int&; param = int&
f7(cx); // T = int const&; param = int const&
f7(rx); // T = int&; param = int&
f7(rx2); // T = int&; param = int&
f7(rcx); // T = int const&; param = int const&
f7(px); // T = int*&; param = int*&
f7(pcx); // T = int const*&; param = int const*&
f7(cpx); // T = int* const&; param = int* const&
f7(22); // T = int; param = int&&
}

const T&&

param一定是常量右值引用,只接收右值。

1
2
3
4
5
template <typename T>
void f8(const T&& param) {
std::cout << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name()
<< "; param = " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}
1
2
3
4
void test_f8() {
std::cout << "const universal reference" << std::endl;
f8(22);
}

auto

丢弃CVR(const、volatile、reference),剩下的就是auto的类型

当传入的是函数名或者数组名时,auto会是函数指针或数组指针

支持大括号初始化,分为直接赋值和拷贝赋值两种情况,c++17之前的编译器,所有推断出来的都是std::initializer_list,之后的编译器,拷贝赋值保持不变,直接赋值不支持多个元素,类型推断为第一个元素的类型

因为当变量类型为auto时,表示变量将会是重新定义的一个全新的变量,与传递进来的变量应该彼此独立,所以auto不能是引用,引用需要丢弃!一个全新的变量,其CV修饰符应该有自身显示定义,不应该继承其它变量的修饰符,所以CV修饰符应该丢弃。

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
void test_f1() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "value" << std::endl;
auto a_x = x; // int
auto a_cx = cx; // int
auto a_rx = rx; // int
auto a_rcx = rcx; // int
auto a_px = px; // int*
auto a_pcx = pcx; // const int*
auto a_cpx = cpx; // int*
auto a_22 = 22; // int

auto a_test = test; // void (*)(int)
auto a_arr = arr; // int*

auto a_init = {1, 2, 3}; // std::initializer_list<int>
// auto a_init2{1, 2, 3}; // error
auto a_init3 = {1}; // std::initializer_list<int>
auto a_init4{1}; // int
}

auto&/auto*

auto&

丢弃引用,剩下的就是auto的类型(继承CV)

当传入的是函数名或者数组名时,T会是函数引用或数组引用
因为变量的类型是auto&,已经有引用了,所以当传进来的变量是引用时,应该忽略引用;变量是引用时,赋值是指向外部的变量本身,所以CV描述符不应该丢弃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void test_f2() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "reference" << std::endl;
auto& a_x = x; // int&
auto& a_cx = cx; // const int&
auto& a_rx = rx; // int&
auto& a_rcx = rcx; // const int&
auto& a_px = px; // int*&
auto& a_pcx = pcx; // const int*&
auto& a_cpx = cpx; // int *const &
// auto& a_22 = 22; // error

auto& a_test = test; // void (&)(int)
auto& a_arr = arr; // int &[3]
}

const auto&

当显示声明CV修饰符时,丢弃CV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void test_f3() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "const reference" << std::endl;
const auto& a_x = x; // const int&
const auto& a_cx = cx; // const int&
const auto& a_rx = rx; // const int&
const auto& a_rcx = rcx; // const int&
const auto& a_px = px; // int *const&
const auto& a_pcx = pcx; // const int *const&
const auto& a_cpx = cpx; // int *const&
const auto& a_22 = 22; // const int&

const auto& a_test = test; // void(&)(int)
const auto& a_arr = arr; // const int&[3]
}

auto*

丢弃指针,丢弃CV,剩下的就是auto的类型

因为变量的类型是auto*,已经有指针了,所以当传进来的变量是指针时,应该忽略指针;变量是指针就表明是一个全新的变量,其CV修饰符应该有自身显示定义,不应该继承其它变量的修饰符,所以CV修饰符应该丢弃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test_f4() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "pointer" << std::endl;
auto* a_px = px; // int*
auto* a_pcx = pcx; // const int*
auto* a_cpx = cpx; // int*

auto* a_test = test; // void(*)(int)
auto* a_arr = arr; // int*
}

auto&&

表达式为左值时,auto为表达式类型(丢弃引用后)的左值引用;表达式为右值时,auto为表达式类型。(继承CV)

注意:auto变量看起来是右值引用,但并不一定是右值引用,只有当表达式为右值时,才是右值引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void test_f7() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "universal reference" << std::endl;
auto&& a_x = x; // int&
auto&& a_cx = cx; // const int&
auto&& a_rx = rx; // int&
auto&& a_rcx = rcx; // const int&
auto&& a_px = px; // int*&
auto&& a_pcx = pcx; // const int*&
auto&& a_cpx = cpx; // int *const&
auto&& a_22 = 22; // int&&
}

const auto&&

只接收右值,一定是右值引用。

1
2
3
4
void test_f8() {
std::cout << "const universal reference" << std::endl;
const auto&& a_22 = 22; // const int&&
}

lambda capture

reference

引用捕捉,使用模板T&规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test_f1() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "reference capture" << std::endl;
auto lam = [&x] {};
auto lam2 = [&cx] {};
auto lam3 = [&rx] {};
auto lam4 = [&rcx] {};
}
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
71
72
73
74
75
76
77
{
class __lambda_16_14
{
public:
inline void operator()() const
{
}

private:
int & x;
public:
// inline /*constexpr */ __lambda_16_14(__lambda_16_14 &&) noexcept = default;
__lambda_16_14(int & _x)
: x{_x}
{}

};

__lambda_16_14 lam = __lambda_16_14(__lambda_16_14{x});

class __lambda_17_15
{
public:
inline void operator()() const
{
}

private:
const int & cx;
public:
// inline /*constexpr */ __lambda_17_15(__lambda_17_15 &&) noexcept = default;
__lambda_17_15(const int & _cx)
: cx{_cx}
{}

};

__lambda_17_15 lam2 = __lambda_17_15(__lambda_17_15{cx});

class __lambda_18_15
{
public:
inline void operator()() const
{
}

private:
int & rx;
public:
// inline /*constexpr */ __lambda_18_15(__lambda_18_15 &&) noexcept = default;
__lambda_18_15(int & _rx)
: rx{_rx}
{}

};

__lambda_18_15 lam3 = __lambda_18_15(__lambda_18_15{rx});

class __lambda_19_15
{
public:
inline void operator()() const
{
}

private:
const int & rcx;
public:
// inline /*constexpr */ __lambda_19_15(__lambda_19_15 &&) noexcept = default;
__lambda_19_15(const int & _rcx)
: rcx{_rcx}
{}

};

__lambda_19_15 lam4 = __lambda_19_15(__lambda_19_15{rcx});
}

value

值捕捉,使用模板T规则,但是继承CV!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void test_f2() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "value capture" << std::endl;
auto lam = [x] {};
auto lam2 = [cx] {};
auto lam3 = [rx] {};
auto lam4 = [rcx] {};
auto lam5 = [px] {};
auto lam6 = [pcx] {};
auto lam7 = [cpx] {};
}
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
{
class __lambda_16_14
{
public:
inline void operator()() const
{
}

private:
int x;
public:
// inline /*constexpr */ __lambda_16_14(__lambda_16_14 &&) noexcept = default;
__lambda_16_14(int & _x)
: x{_x}
{}

};

__lambda_16_14 lam = __lambda_16_14(__lambda_16_14{x});

class __lambda_17_15
{
public:
inline void operator()() const
{
}

private:
const int cx;
public:
// inline /*constexpr */ __lambda_17_15(__lambda_17_15 &&) noexcept = default;
__lambda_17_15(const int & _cx)
: cx{_cx}
{}

};

__lambda_17_15 lam2 = __lambda_17_15(__lambda_17_15{cx});

class __lambda_18_15
{
public:
inline void operator()() const
{
}

private:
int rx;
public:
// inline /*constexpr */ __lambda_18_15(__lambda_18_15 &&) noexcept = default;
__lambda_18_15(int & _rx)
: rx{_rx}
{}

};

__lambda_18_15 lam3 = __lambda_18_15(__lambda_18_15{rx});

class __lambda_19_15
{
public:
inline void operator()() const
{
}

private:
const int rcx;
public:
// inline /*constexpr */ __lambda_19_15(__lambda_19_15 &&) noexcept = default;
__lambda_19_15(const int & _rcx)
: rcx{_rcx}
{}

};

__lambda_19_15 lam4 = __lambda_19_15(__lambda_19_15{rcx});

class __lambda_20_15
{
public:
inline void operator()() const
{
}

private:
int * px;
public:
// inline /*constexpr */ __lambda_20_15(__lambda_20_15 &&) noexcept = default;
__lambda_20_15(int * _px)
: px{_px}
{}

};

__lambda_20_15 lam5 = __lambda_20_15(__lambda_20_15{px});

class __lambda_21_15
{
public:
inline void operator()() const
{
}

private:
const int * pcx;
public:
// inline /*constexpr */ __lambda_21_15(__lambda_21_15 &&) noexcept = default;
__lambda_21_15(const int * _pcx)
: pcx{_pcx}
{}

};

__lambda_21_15 lam6 = __lambda_21_15(__lambda_21_15{pcx});

class __lambda_22_15
{
public:
inline void operator()() const
{
}

private:
int *const cpx;
public:
// inline /*constexpr */ __lambda_22_15(__lambda_22_15 &&) noexcept = default;
__lambda_22_15(int *const _cpx)
: cpx{_cpx}
{}

};

__lambda_22_15 lam7 = __lambda_22_15(__lambda_22_15{cpx});
}

init capture

初始化捕捉,使用auto规则。

C++14的扩展,在std=c++11时也可使用,但是会报警告warning: initialized lambda captures are a C++14 extension [-Wc++14-extensions]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void test_f3() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "init capture" << std::endl;
auto lam = [x = x] {};
auto lam2 = [cx = cx] {};
auto lam3 = [rx = rx] {};
auto lam4 = [rcx = rcx] {};
auto lam5 = [px = px] {};
auto lam6 = [pcx = pcx] {};
auto lam7 = [cpx = cpx] {};
auto lam8 = [&x = x] {};
auto lam9 = [&cx = cx] {};
auto lam10 = [&rx = rx] {};
auto lam11 = [&rcx = rcx] {};
}
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
{
class __lambda_16_14
{
public:
inline void operator()() const
{
}

private:
int x;
public:
// inline /*constexpr */ __lambda_16_14(__lambda_16_14 &&) noexcept = default;
__lambda_16_14(int & _x)
: x{_x}
{}

};

__lambda_16_14 lam = __lambda_16_14(__lambda_16_14{x});

class __lambda_17_15
{
public:
inline void operator()() const
{
}

private:
int cx;
public:
// inline /*constexpr */ __lambda_17_15(__lambda_17_15 &&) noexcept = default;
__lambda_17_15(const int & _cx)
: cx{_cx}
{}

};

__lambda_17_15 lam2 = __lambda_17_15(__lambda_17_15{cx});

class __lambda_18_15
{
public:
inline void operator()() const
{
}

private:
int rx;
public:
// inline /*constexpr */ __lambda_18_15(__lambda_18_15 &&) noexcept = default;
__lambda_18_15(int & _rx)
: rx{_rx}
{}

};

__lambda_18_15 lam3 = __lambda_18_15(__lambda_18_15{rx});

class __lambda_19_15
{
public:
inline void operator()() const
{
}

private:
int rcx;
public:
// inline /*constexpr */ __lambda_19_15(__lambda_19_15 &&) noexcept = default;
__lambda_19_15(const int & _rcx)
: rcx{_rcx}
{}

};

__lambda_19_15 lam4 = __lambda_19_15(__lambda_19_15{rcx});

class __lambda_20_15
{
public:
inline void operator()() const
{
}

private:
int * px;
public:
// inline /*constexpr */ __lambda_20_15(__lambda_20_15 &&) noexcept = default;
__lambda_20_15(int * _px)
: px{_px}
{}

};

__lambda_20_15 lam5 = __lambda_20_15(__lambda_20_15{px});

class __lambda_21_15
{
public:
inline void operator()() const
{
}

private:
const int * pcx;
public:
// inline /*constexpr */ __lambda_21_15(__lambda_21_15 &&) noexcept = default;
__lambda_21_15(const int * _pcx)
: pcx{_pcx}
{}

};

__lambda_21_15 lam6 = __lambda_21_15(__lambda_21_15{pcx});

class __lambda_22_15
{
public:
inline void operator()() const
{
}

private:
int * cpx;
public:
// inline /*constexpr */ __lambda_22_15(__lambda_22_15 &&) noexcept = default;
__lambda_22_15(int * _cpx)
: cpx{_cpx}
{}

};

__lambda_22_15 lam7 = __lambda_22_15(__lambda_22_15{cpx});

class __lambda_23_15
{
public:
inline void operator()() const
{
}

private:
int & x;
public:
// inline /*constexpr */ __lambda_23_15(__lambda_23_15 &&) noexcept = default;
__lambda_23_15(int & _x)
: x{_x}
{}

};

__lambda_23_15 lam8 = __lambda_23_15(__lambda_23_15{x});

class __lambda_24_15
{
public:
inline void operator()() const
{
}

private:
const int & cx;
public:
// inline /*constexpr */ __lambda_24_15(__lambda_24_15 &&) noexcept = default;
__lambda_24_15(const int & _cx)
: cx{_cx}
{}

};

__lambda_24_15 lam9 = __lambda_24_15(__lambda_24_15{cx});

class __lambda_25_16
{
public:
inline void operator()() const
{
}

private:
int & rx;
public:
// inline /*constexpr */ __lambda_25_16(__lambda_25_16 &&) noexcept = default;
__lambda_25_16(int & _rx)
: rx{_rx}
{}

};

__lambda_25_16 lam10 = __lambda_25_16(__lambda_25_16{rx});

class __lambda_26_16
{
public:
inline void operator()() const
{
}

private:
const int & rcx;
public:
// inline /*constexpr */ __lambda_26_16(__lambda_26_16 &&) noexcept = default;
__lambda_26_16(const int & _rcx)
: rcx{_rcx}
{}

};

__lambda_26_16 lam11 = __lambda_26_16(__lambda_26_16{rcx});
}

decltype

decltype对类型非常敏感,针对标识名和表达式的推断规则是不一样的。

1
2
3
4
5
int x = 22;

std::cout << boost::typeindex::type_id_with_cvr<decltype(x)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype((x))>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(*&x)>().pretty_name() << std::endl;

标识名

对于标识名,得到声明的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void test_f1() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int&& rrx = 22;
int arr[3];
std::cout << "decltype name" << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(x)>().pretty_name() << std::endl; // int
std::cout << boost::typeindex::type_id_with_cvr<decltype(cx)>().pretty_name() << std::endl; // int const
std::cout << boost::typeindex::type_id_with_cvr<decltype(rx)>().pretty_name() << std::endl; // int&
std::cout << boost::typeindex::type_id_with_cvr<decltype(rcx)>().pretty_name() << std::endl; // int const&
std::cout << boost::typeindex::type_id_with_cvr<decltype(px)>().pretty_name() << std::endl; // int*
std::cout << boost::typeindex::type_id_with_cvr<decltype(pcx)>().pretty_name() << std::endl; // const int*
std::cout << boost::typeindex::type_id_with_cvr<decltype(cpx)>().pretty_name() << std::endl; // int* const
std::cout << boost::typeindex::type_id_with_cvr<decltype(test)>().pretty_name() << std::endl; // void(int)
std::cout << boost::typeindex::type_id_with_cvr<decltype(arr)>().pretty_name() << std::endl; // int[3]
std::cout << boost::typeindex::type_id_with_cvr<decltype(rrx)>().pretty_name() << std::endl; // int&&
// std::cout << boost::typeindex::type_id_with_cvr<decltype({1, 2, 3})>().pretty_name() << std::endl;
}

表达式

亡值

表达式值为亡值,类型为T&&。

1
2
3
4
5
void test_f2() {
int x = 22;
std::cout << "decltype expr" << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(std::move(x))>().pretty_name() << std::endl; // int&&
}

左值

表达式值为左值,类型为T&。

1
2
3
4
5
6
void test_f2() {
int x = 22;
std::cout << "decltype expr" << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype((x))>().pretty_name() << std::endl; // int&
std::cout << boost::typeindex::type_id_with_cvr<decltype(*&x)>().pretty_name() << std::endl; // int&
}

纯右值

表达式值为纯右值,类型为T。

1
2
3
4
5
6
7
void test_f2() {
int x = 22;
std::cout << "decltype expr" << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(22)>().pretty_name() << std::endl; // int
std::cout << boost::typeindex::type_id_with_cvr<decltype(std::shared_ptr<int>())>().pretty_name() << std::endl; // std::shared_ptr<int>
}

function return

lambda function

使用模板T的规则,去掉CVR,剩下的就是函数返回类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void test_f1() {
int x = 22;
const int cx = x;
int& rx = x;
const int& rcx = cx;
int* px = &x;
const int* pcx = &cx;
int* const cpx = &x;
int arr[3];
std::cout << "lambda function return" << std::endl;
auto lam = [x] { return x; };
auto lam2 = [cx] { return cx; };
auto lam3 = [rx] { return rx; };
auto lam4 = [rcx] { return rcx; };
auto lam8 = [&x = x] { return x; };
auto lam9 = [&cx = cx] { return cx; };
auto lam10 = [&rx = rx] { return rx; };
auto lam11 = [&rcx = rcx] { return rcx; };
}
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
{
class __lambda_16_14
{
public:
inline int operator()() const
{
return x;
}

private:
int x;
public:
// inline /*constexpr */ __lambda_16_14(__lambda_16_14 &&) noexcept = default;
__lambda_16_14(int & _x)
: x{_x}
{}

};

__lambda_16_14 lam = __lambda_16_14(__lambda_16_14{x});

class __lambda_17_15
{
public:
inline int operator()() const
{
return cx;
}

private:
const int cx;
public:
// inline /*constexpr */ __lambda_17_15(__lambda_17_15 &&) noexcept = default;
__lambda_17_15(const int & _cx)
: cx{_cx}
{}

};

__lambda_17_15 lam2 = __lambda_17_15(__lambda_17_15{cx});

class __lambda_18_15
{
public:
inline int operator()() const
{
return rx;
}

private:
int rx;
public:
// inline /*constexpr */ __lambda_18_15(__lambda_18_15 &&) noexcept = default;
__lambda_18_15(int & _rx)
: rx{_rx}
{}

};

__lambda_18_15 lam3 = __lambda_18_15(__lambda_18_15{rx});

class __lambda_19_15
{
public:
inline int operator()() const
{
return rcx;
}

private:
const int rcx;
public:
// inline /*constexpr */ __lambda_19_15(__lambda_19_15 &&) noexcept = default;
__lambda_19_15(const int & _rcx)
: rcx{_rcx}
{}

};

__lambda_19_15 lam4 = __lambda_19_15(__lambda_19_15{rcx});

class __lambda_20_15
{
public:
inline int operator()() const
{
return x;
}

private:
int & x;
public:
// inline /*constexpr */ __lambda_20_15(__lambda_20_15 &&) noexcept = default;
__lambda_20_15(int & _x)
: x{_x}
{}

};

__lambda_20_15 lam8 = __lambda_20_15(__lambda_20_15{x});

class __lambda_21_15
{
public:
inline int operator()() const
{
return cx;
}

private:
const int & cx;
public:
// inline /*constexpr */ __lambda_21_15(__lambda_21_15 &&) noexcept = default;
__lambda_21_15(const int & _cx)
: cx{_cx}
{}

};

__lambda_21_15 lam9 = __lambda_21_15(__lambda_21_15{cx});

class __lambda_22_16
{
public:
inline int operator()() const
{
return rx;
}

private:
int & rx;
public:
// inline /*constexpr */ __lambda_22_16(__lambda_22_16 &&) noexcept = default;
__lambda_22_16(int & _rx)
: rx{_rx}
{}

};

__lambda_22_16 lam10 = __lambda_22_16(__lambda_22_16{rx});

class __lambda_23_16
{
public:
inline int operator()() const
{
return rcx;
}

private:
const int & rcx;
public:
// inline /*constexpr */ __lambda_23_16(__lambda_23_16 &&) noexcept = default;
__lambda_23_16(const int & _rcx)
: rcx{_rcx}
{}

};

__lambda_23_16 lam11 = __lambda_23_16(__lambda_23_16{rcx});
}

auto

返回类型是auto时,使用auto规则。

C++11

在C++11上,只能通过返回值后置来实现,局限太大。

1
2
3
4
5
template <typename U, typename T>
auto Add(U u, T t) -> decltype(u + t) {
auto val = u + t;
return val;
}

C++14

在C++14后只需要返回类型写auto就可以了。

1
2
3
4
auto lookupvalue(const int index) {
static std::vector<int> values = {1, 2, 3};
return values[index]; // int
}

decltype(auto)

返回类型是decltype(auto)时,使用decltype规则。

1
2
3
4
decltype(auto) lookupvalue2(const int index) {
static std::vector<int> values = {1, 2, 3};
return values[index]; // int&
}
1
2
3
4
5
decltype(auto) lookupvalue4(const int index) {
static std::vector<int> values = {1, 2, 3};
auto val = values[index];
return (val);
}