(追記) (追記ここまで)
Aug 16, 2017

C++: Приведение Enum структур к общему виду в шаблонах

Задача - создать шаблонную функцию, в которой в зависимости от типа перечисления выбирается нужный индекс в контейнере vector. Попробуем решить задачу в лоб, создадим 3 перечисления и выберем нужный индекс в шаблоне.

Использование enum в шаблоне напрямую
 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
#include <vector>
#include <iostream> // cout
struct PEOPLE {
 enum {
 PEOPLE_ID, // 0
 FIRSTNAME, // 1
 LASTNAME // 2
 };
};
struct STUDENT {
 enum {
 LASTNAME, // 0
 FIRSTNAME, // 1
 SALARY, // 2
 _ID, // 3
 };
};
struct EMPLOYE {
 enum {
 LASTNAME, // 0
 _ID, // 1
 FIRSTNAME, // 2
 SALARY // 3
 };
};
typedef std::vector<std::string> t_item;
template <class T>
void printHuman(t_item item, T _type)
{
 std::string salary = "0.00 $";
 std::string human_id = "-1";
 const bool isStudent = std::is_same<T, STUDENT>::value;
 const bool isEmploye = std::is_same<T, EMPLOYE>::value;
 // PEOPLE
 if (std::is_same<T, PEOPLE>::value) {
 human_id = item.at(PEOPLE::PEOPLE_ID);
 }
 // STUDENT || EMPLOYE
 else if (isStudent || isEmploye) {
 // Выбираем поле в зависимости от типа структуры
 salary = item.at(T::SALARY);
 human_id = item.at(T::_ID);
 }
 // Отобажаем на экране
 std::cout << human_id
 << "\t\t"
 << item.at(T::FIRSTNAME)
 << "\t\t"
 << item.at(T::LASTNAME)
 << "\t\t"
 << salary
 << std::endl;
}
int main()
{
 // Создаем людей разных типов
 t_item vasya{"1", "Vasya", "Vasnetsov"}; // PEOPLE
 t_item petya{"Иванов", "Petr", "10.00 $", "13"}; // STUDENT
 t_item bjarne{"Бьёрн", "66", "Straustrup", "100 000.00 $"}; // EMPLOYE
 // Выводим информацию о людях
 printHuman(vasya, PEOPLE());
 printHuman(petya, STUDENT());
 printHuman(bjarne, EMPLOYE());
 return 0;
}

Скомпилируем пример с добавлением C++11.

Попытка компиляции enum в шаблоне
$ g++ -std=c++11 main.cpp -o enum-example
main-bad.cpp: In instantiation of ‘void printHuman(t_item, T) [with T = PEOPLE; t_item = std::vector<std::__cxx11::basic_string<char> >]’:
main-bad.cpp:78:30: required from here
main-bad.cpp:55:14: error: ‘SALARY’ is not a member of ‘PEOPLE’
 salary = item.at(T::SALARY);
 ^
main-bad.cpp:56:14: error: ‘_ID’ is not a member of ‘PEOPLE’
 human_id = item.at(T::_ID);
 ^
Makefile:7: recipe for target 'bad' failed
make: *** [bad] Error 1

Получаем ошибку, т.к. подстановка типа перечисления PEOPLE не имеет атрибутов _ID и SALARY.

Что бы обойти эту проблему выведем тип с помощью std::conditional.

Использование enum в шаблоне через выведение типа енума
 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
void printHuman(t_item item, T _type)
{
 std::string salary = "0.00 $";
 std::string human_id = "-1";
 const bool isStudent = std::is_same<T, STUDENT>::value;
 const bool isEmploye = std::is_same<T, EMPLOYE>::value;
 // PEOPLE
 if (std::is_same<T, PEOPLE>::value) {
 human_id = item.at(PEOPLE::PEOPLE_ID);
 }
 // STUDENT || EMPLOYE
 else if (isStudent || isEmploye) {
 // Выбираем поле в зависимости от типа структуры
 typedef std::conditional<isStudent, STUDENT, EMPLOYE> cond;
 salary = item.at(cond::type::SALARY);
 human_id = item.at(cond::type::_ID);
 }
 // Отобажаем на экране
 std::cout << human_id
 << "\t\t"
 << item.at(T::FIRSTNAME)
 << "\t\t"
 << item.at(T::LASTNAME)
 << "\t\t"
 << salary
 << std::endl;
}

В результате программа скомпилируется без ошибок. Это происходит потому, что conditional реализует свой шаблон на базе только двух нужных нам enum’ов (STUDENT, EMPLOY).

Компиляция и запуск
$ g++ -std=c++11 main.cpp -o enum-example
$ ./enum-example
1 Vasya Vasnetsov 0.00 $
13 Petr Иванов 10.00 $
66 Straustrup Бьёрн 100 000.00 $

AltStyle によって変換されたページ (->オリジナル) /