c++17通过模板获取类成员的个数
通过下面的模板可以获取一个类中成员变量的个数,(包含数组时会有错误,如int a[3]会计算成3个成员)
#include <iostream>
#include <type_traits>
#include <string>
using namespace std;
struct AnyType {
template<typename T>
operator T();
};
// 通用的CountMember
template<typename T, typename=void, typename ...Ts>
struct CountMember{
constexpr static size_t value = sizeof ...(Ts) - 1;
};
// 这是偏特化的CountMember,优先匹配它,具体原理参考下面的“优先匹配”章节
template<typename T, typename ...Ts>
struct CountMember<T, std::void_t<decltype(T{Ts{}...})>, Ts...>
{
constexpr static size_t value = CountMember<T, void, Ts..., AnyType>::value;
};
int main() {
struct Mem3{int a; char b; string c;};
std::cout<<CountMember<Mem3>::value << endl; //3
// 其主要原理是通过匹配Mem3的初始化列表的个数来计算成员的个数。
// 我们可以尝试用初始化列表构造Mem3对象, 初始化列表最大时,即得到CountMember::value
{
Mem3 t{};
printf("t.a=%d\n", t.a);
}
{
Mem3 t{1, 'c', "abd"}; //初始化列表最多3个member
printf("t.a=%d\n", t.a);
}
// 当成员中含有数组时,例如下面的Mem3Array,初始化列表member个数最多为5
struct Mem3Array{int a; char b; string c[3];};
std::cout<<CountMember<Mem3Array>::value << endl; //5
{
Mem3Array t{1, 'b'};
printf("t.a=%d\n", t.a);
}
{
Mem3Array t{1, 'b', "abc", "def"};
printf("t.a=%d\n", t.a);
}
{
Mem3Array t{1, 'b', "abc", "def", "ghi"}; //初始化列表member个数最多为5
printf("t.a=%d\n", t.a);
}
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
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
# 展开解释
上面的可变参数模板类CountMember相当于创建了下面的多个CM类, 以含有两个成员的struct Mem2为例展开解释:
#include <iostream>
#include <type_traits>
#include <string>
using namespace std;
struct AnyType {
template<typename T>
operator T();
};
// 记该类为struct_final
template<typename T, typename=void, typename ...Ts>
struct CM{
constexpr static size_t value = sizeof ...(Ts) - 1;
};
// 记该类为struct3
template<typename T>
struct CM<T, std::void_t<decltype(T{ AnyType{},AnyType{},AnyType{} })>, AnyType, AnyType, AnyType> //简化为<T, void, AnyType, AnyType, AnyType>
{ //因为Mem2不可由含3个member的初始化列表创建,所以里面的decltype匹配不成功,偏特化匹配失败,该类不会被创建,
constexpr static size_t value = CM<T, void, AnyType, AnyType, AnyType, AnyType>::value;
};
// 记该类为struct2
template<typename T>
struct CM<T, std::void_t<decltype(T{ AnyType{},AnyType{} })>, AnyType, AnyType> //简化为<T, void, AnyType, AnyType>,
{ //因为Mem2可由含2个member的初始化列表创建,所以里面的decltype匹配成功
// 然后根据可变参数模板尝试匹配上面的struct3,匹配不成功,因而匹配struct_final
// 得到 value = 3-1 = 2
constexpr static size_t value = CM<T, void, AnyType, AnyType, AnyType>::value; // 匹配struct_final
};
// 记该类为struct1
template<typename T>
struct CM<T, std::void_t<decltype(T{ AnyType{} })>, AnyType> //简化为<T, void, AnyType>,
{ //因为Mem2可由含1个member的初始化列表创建,所以里面的decltype匹配成功
constexpr static size_t value = CM<T, void, AnyType, AnyType>::value; //然后根据可变参数模板创建了上面的struct2
};
// 记该类为struct0
template<typename T>
struct CM<T, std::void_t<>> //Mem2 首先根据可变参数模板匹配该类,<T,void>, 可变参数数量为0
{
constexpr static size_t value = CM<T, void, AnyType>::value; //然后根据可变参数模板创建了上面的struct1
};
int main() {
struct Mem2{int a; char b;};
std::cout<<"CM<Mem2>="<<CM<Mem2>::value << endl; //匹配struct0, 输出为2
}
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
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
# 优先匹配
下面解释偏特化的优先匹配
#include <iostream>
#include <type_traits>
#include <string>
using namespace std;
// 通用的TestSpec
template<typename T, typename=void>
struct TestSpec {
constexpr static int value = 1;
};
//这是偏特化的TestSpec, 优先匹配它。即便它和通用的TestSpec都接收<T>类型的模板参数
template<typename T>
struct TestSpec<T, void> {
constexpr static int value = 2;
};
int main() {
cout<<TestSpec<char>::value<<endl; //输出2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54