Evgenii Legotckoi
26 лютого 2019 р. 16:22

Використання SFINAE для спеціалізації шаблонних методів, залежно від типу контейнера std::map або std::vector, передається як аргумент

Виникла проблема: "написати другу реалізацію шаблонної функції", в якій передаються ті самі аргументи. Тобто сигнатура обох функцій повністю збігається. Відмінність полягає в тому, що як аргумент функції можуть бути передані контейнери std::vector і контейнери std::map. І в залежності від того, std::vector це чи std::map, слід вибирати ту чи іншу реалізацію.

У цьому випадку буде використовуватися правило SFINAE, яке говорить: Якщо кінцеві типи аргументів не можуть бути обчислені (виконати підстановку параметрів шаблону) перевантаженої шаблонної функції, компілятор не видає помилку, а шукає інше відповідне навантаження. Помилка буде у трьох випадках:

  • Не знайдено відповідного навантаження.
  • Було знайдено кілька таких перевантажень, і С++ не може вирішити, яку взяти.
  • Виявлено перевантаження, виявилося, що це шаблон, і при створенні екземпляра шаблону сталася помилка.

Наш випадок буде другим. Існують дві функції шаблону навантаження.


Реалізація

Для визначення необхідного навантаження буде використано std::enable_if , який використовується якраз для таких випадків для спеціалізації тієї чи іншої функції залежно від типу даних, які потрібно обробити.

Стандартна бібліотека STD, як виявилося, не має функціоналу для визначення, чи є контейнер std::map чи ні. Але на StackOverflow це питання вже обговорювалося. Наведений там код потрібно лише трохи підправити, і правильно застосувати для написання спеціалізації функції залежно від типу контейнера.

Суть коду полягає в тому, щоб на етапі компіляції вирішити, чи є елемент контейнера std::pair і на підставі цього вирішити, чи контейнер std::map. Якщо контейнер std::map виберіть одну функцію; в інших випадках виберіть другу реалізацію функції.

  1. #include <iterator>
  2. #include <type_traits>
  3.  
  4. // templates for determining whether the std::pair container element is
  5. template <typename T>
  6. struct is_pair : std::false_type {};
  7.  
  8. template <typename T, typename U>
  9. struct is_pair<std::pair<T, U>> : std::true_type {};
  10.  
  11. template <typename T>
  12. constexpr bool is_pair_v = is_pair<T>::value;
  13.  
  14. // templates for determining if the container is std::map
  15. template<typename, typename = void>
  16. struct is_mapping : std::false_type {};
  17.  
  18. template <typename Container>
  19. struct is_mapping<Container, std::enable_if_t<is_pair_v<typename std::iterator_traits<typename Container::iterator>::value_type>>> : std::true_type {};
  20.  
  21. template <typename T>
  22. constexpr bool is_mapping_v = is_mapping<T>::value;
  23.  
  24. #include <map>
  25. #include <vector>
  26. #include <iostream>
  27.  
  28. class ClassWithSpecializedMethods
  29. {
  30. public:
  31. ClassWithSpecializedMethods() {}
  32.  
  33. // A specialized method for handling std::map
  34. template <class ContainerType>
  35. static typename std::enable_if<is_mapping_v<ContainerType>, void>::type printContainer(ContainerType& container);
  36.  
  37. // A specialized method for handling std::vector
  38. template <class ContainerType>
  39. static typename std::enable_if<!is_mapping_v<ContainerType>, void>::type printContainer(ContainerType& container);
  40. };
  41.  
  42. // Method implementations
  43. template <class ContainerType>
  44. inline
  45. static typename std::enable_if<is_mapping_v<ContainerType>, void>::type ClassWithSpecializedMethods::printContainer(ContainerType& container)
  46. {
  47. std::cout << "Map:" << std::endl;
  48. for (const auto& [key, value] : container)
  49. {
  50. std::cout << "Key: " << key << " Value: " << value << std::endl;
  51. }
  52. std::cout << std::endl;
  53. }
  54.  
  55. template <class ContainerType>
  56. inline
  57. static typename std::enable_if<!is_mapping_v<ContainerType>, void>::type ClassWithSpecializedMethods::printContainer(ContainerType& container)
  58. {
  59. std::cout << "Vector:" << std::endl;
  60. for (const auto& value : container)
  61. {
  62. std::cout << "Value: " << value << std::endl;
  63. }
  64. std::cout << std::endl;
  65. }
  66.  
  67. // Testing functions
  68. int main() {
  69. std::cout << "is_pair:" << std::endl;
  70. std::cout << "Map: " << is_pair_v<std::iterator_traits<std::map<int, int>::iterator>::value_type> << std::endl;
  71. std::cout << "Vector: " << is_pair_v<std::iterator_traits<std::vector<int>::iterator>::value_type> << std::endl;
  72. std::cout << std::endl;
  73. std::cout << "is_mapping:" << std::endl;
  74. std::cout << "Map: " << is_mapping_v<std::map<int, int>> << std::endl;
  75. std::cout << "Vector: " << is_mapping_v<std::vector<int>> << std::endl;
  76. std::cout << std::endl;
  77.  
  78. std::map<int, int> map_container = {{1, 1}, {2, 2}, {3, 3}};
  79. std::vector<int> vector_container = {1, 2, 3};
  80.  
  81. ClassWithSpecializedMethods::printContainer(map_container);
  82. ClassWithSpecializedMethods::printContainer(vector_container);
  83. }
  84.  

Використовувані стандарти C++

Щоб цей код працював, необхідно використовувати стандарт C++17; на меншому стандарті код не компілюватиметься.

У проектах QMake до файлу pro слід додати наступні рядки

  1. CONFIG += console c++17
  2. QMAKE_CXXFLA
  3.  
  4. ## Conclusion
  5.  
  6. As a conclusion, we get the following conclusion
  7.  
  8.  
  9. ```lang-bsh
  10. is_pair:
  11. Карта: 1
  12. Вектор: 0
  13.  
  14. is_mapping:
  15. Карта: 1
  16. Вектор: 0
  17.  
  18. Карта:
  19. Ключ: 1 Значення: 1
  20. Ключ: 2 Значення: 2
  21. Ключ: 3 Значення: 3
  22.  
  23. Вектор:
  24. Значення: 1
  25. Значення: 2
  26. Значення: 3

Как это выглядит в консоли

Вывод с использование SFINAE для std::map и std::vector

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
  • Останні коментарі
  • Evgenii Legotckoi
    16 квітня 2025 р. 17:08
    Благодарю за отзыв. И вам желаю всяческих успехов!
  • IscanderChe
    12 квітня 2025 р. 17:12
    Добрый день. Спасибо Вам за этот проект и отдельно за ответы на форуме, которые мне очень помогли в некоммерческих пет-проектах. Профессиональным программистом я так и не стал, но узнал мно…
  • AK
    01 квітня 2025 р. 11:41
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    09 березня 2025 р. 21:02
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    09 березня 2025 р. 16:14
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…