Применение SFINAE для специализации шаблонных методов в зависимости от типа контейнера std::map или std::vector, передаваемых в качестве аргумента

SFINAE, C++17, template

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

В данном случае будет использоваться правило SFINAE, которое гласит: Если не получается рассчитать окончательные типы аргументов (провести подстановку шаблонных параметров) перегруженной шаблонной функции, компилятор не выбрасывает ошибку, а ищет другую подходящую перегрузку. Ошибка будет в трёх случаях:

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

Наш случай будет вторым. Имеется две перегрузки шаблонной функции.

Реализация

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

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

Суть кода заключается в том, чтобы на этапе компиляции решить, является ли элемент контейнера std::pair и на основании этого решить, является ли контейнер std::map. Если контейнер является std::map, то выбираем одну функцию, в остальных случаях выбираем вторую реализацию функции.

#include <iterator>
#include <type_traits>

// шаблоны для определения является ли элемент контейнера std::pair
template <typename T>
struct is_pair : std::false_type {};

template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type {};

template <typename T>
constexpr bool is_pair_v = is_pair<T>::value;

// шаблоны для определения является ли контейнера std::map
template<typename, typename = void>
struct is_mapping : std::false_type {};

template <typename Container>
struct is_mapping<Container, std::enable_if_t<is_pair_v<typename  std::iterator_traits<typename Container::iterator>::value_type>>> : std::true_type {};

template <typename T>
constexpr bool is_mapping_v = is_mapping<T>::value;

#include <map>
#include <vector>
#include <iostream>

class ClassWithSpecializedMethods
{
public:
    ClassWithSpecializedMethods() {}

    // Специализированный метод для обработки std::map
    template <class ContainerType>
    static typename std::enable_if<is_mapping_v<ContainerType>, void>::type printContainer(ContainerType& container);

    // Специализированный метод для обработки std::vector
    template <class ContainerType>
    static typename std::enable_if<!is_mapping_v<ContainerType>, void>::type printContainer(ContainerType& container);
};

// Реализации методов
template <class ContainerType>
inline
static typename std::enable_if<is_mapping_v<ContainerType>, void>::type ClassWithSpecializedMethods::printContainer(ContainerType& container)
{
    std::cout << "Map:" << std::endl;
    for (const auto& [key, value] : container)
    {
        std::cout << "Key: " << key << " Value: " << value << std::endl;
    }
    std::cout << std::endl;
}

template <class ContainerType>
inline
static typename std::enable_if<!is_mapping_v<ContainerType>, void>::type ClassWithSpecializedMethods::printContainer(ContainerType& container)
{
    std::cout << "Vector:" << std::endl;
    for (const auto& value : container)
    {
        std::cout << "Value: " << value << std::endl;
    }
    std::cout << std::endl;
}

// Тестируем работу функций
int main() {
    std::cout << "is_pair:" << std::endl;
    std::cout << "Map:    " << is_pair_v<std::iterator_traits<std::map<int, int>::iterator>::value_type> << std::endl;
    std::cout << "Vector: " << is_pair_v<std::iterator_traits<std::vector<int>::iterator>::value_type>   << std::endl;
    std::cout << std::endl;
    std::cout << "is_mapping:" << std::endl;
    std::cout << "Map:    " << is_mapping_v<std::map<int, int>> << std::endl;
    std::cout << "Vector: " << is_mapping_v<std::vector<int>>   << std::endl;
    std::cout << std::endl;

    std::map<int, int> map_container = {{1, 1}, {2, 2}, {3, 3}};
    std::vector<int> vector_container = {1, 2, 3};

    ClassWithSpecializedMethods::printContainer(map_container);
    ClassWithSpecializedMethods::printContainer(vector_container);
}

Используемые стандарты C++

Для работы данного кода необходимо использовать стандарт C++17, на меньшем стандарте код не скомпилируется.

В проектах на QMake в pro файл необходимо добавить следующие строки

CONFIG += console c++17
QMAKE_CXXFLAGS += /std:c++17

Заключение

В качестве заключения получаем следующий вывод

is_pair:
Map:    1
Vector: 0

is_mapping:
Map:    1
Vector: 0

Map:
Key: 1 Value: 1
Key: 2 Value: 2
Key: 3 Value: 3

Vector:
Value: 1
Value: 2
Value: 3

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

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

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Поддержать автора Donate

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Donate

Здравствуйте, уважаемые пользователи EVILEG !!!

Если сайт вам помог, то поддержите разработку сайта финансово, пожалуйста.

Вы можете сделать это следующими способами:

Спасибо, Евгений Легоцкой

p
17 февраля 2020 г. 14:41
pstMem

C++ - Тест 003. Условия и циклы

  • Результат:85баллов,
  • Очки рейтинга6
z
17 февраля 2020 г. 6:02
zet

C++ - Тест 006. Перечисления

  • Результат:80баллов,
  • Очки рейтинга4
z
17 февраля 2020 г. 5:49
zet

C++ - Тест 001. Первая программа и типы данных

  • Результат:80баллов,
  • Очки рейтинга4
Последние комментарии
17 февраля 2020 г. 3:22
Евгений Легоцкой

Добрый день. Это кастомный тег, помещается в файл, который находится в каталоге templatetags myapp/ templatetags/ myapp.py
B
16 февраля 2020 г. 13:36
BahaMeirman

Добрый вечер! Монжно по подробней о теге get_companion? ссылка не работает.
16 февраля 2020 г. 9:35
Евгений Легоцкой

Добрый день. На GitHub исходники, можете посмотреть в официальном репозитории
B
16 февраля 2020 г. 9:29
BahaMeirman

Здравстсвуйте Евгений, непонятно мне где эти исходники найти?
Сейчас обсуждают на форуме
ИП
20 февраля 2020 г. 1:24
Игорь Порошин

QSqlQuery query("SELECT CONCAT("#", LPAD(HEX(`t`.RGB), 6, 0)) FROM table AS t");query.exec(); while (query.next()) { QColor(query.value(0).toString()); }
ИП
19 февраля 2020 г. 23:55
Игорь Порошин

qDebug() << model->lastError().text() - возвращает текст и код ошибки, который возвращает база данных, чаще всего ошибка бывает из-за неправильного SQL запроса qDebug() << mo…
19 февраля 2020 г. 8:55
Михаиллл

Можно через сервер сделать
V
19 февраля 2020 г. 7:09
Vitali

Да, прямо сходу не заработало, а сейчас просто некогда разбираться, да и я уже решил не использовать в этом приложении WebEngine. Ошибка: WebEngineContext used before QtWebEngine::initialize(…
19 февраля 2020 г. 7:01
BlinCT

Просто реально не вижу тут каких то проблем в модели, вот вообще ничего. Но она все равно не отображается, то есть ладно бы если данные бы не появлялись а сама таблица была бы. Так и таблиц…
EVILEG
О нас
Услуги
© EVILEG 2015-2019
Рекомендует хостинг TIMEWEB