Почему не работает вывод аргументов шаблона?

Следующая игрушечная программа преобразует тип музыки в соответствующий цвет. Он компилируется и выполняется просто отлично - преобразование COUNTRY терпит неудачу, как и ожидалось, и conversion() функция возвращает значение по умолчанию, WHITE, Однако, если я уберу аргументы шаблона, <MUSIC, COLOR>вычет аргумента шаблона не распознает, какие типы использовать. Как я могу получить отчисления на работу?

#include <map>
#include <iostream>
#include "boost/assign.hpp"

template<typename Key, typename T>
T convert(const Key &k, const T &d, const std::map<Key, T> &m) {
    typename std::map<Key, T>::const_iterator it = m.find(k);
    return it == m.end() ? d : it->second;
}

enum MUSIC { ROCK, RAP, EDM, COUNTRY };
enum COLOR { RED, BLUE, ORANGE, WHITE };

int main()
{
    COLOR c = convert<MUSIC, COLOR>(COUNTRY, WHITE,
        boost::assign::map_list_of (RAP, RED) (EDM, BLUE) (ROCK, RED));
    std::cout << c << std::endl;
}

2 ответа

Решение

boost::assign::map_list_of вероятно не тип map<K,V>, а скорее какой-то тип, конвертируемый в него

Компилятор пытается определить тип как из первых 2 аргументов, так и из последних 1. Последний 1 не имеет смысла, поэтому он сдается.

Мы можем заблокировать удержание последнего аргумента следующим образом:

template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;

template<class T>using block_deduction=type_t<tag<T>>;

template<typename Key, typename T>
T convert(const Key &k, const T &d, const block_deduction<std::map<Key, T>> &m) {
  typename std::map<Key, T>::const_iterator it = m.find(k);
  return it == m.end() ? d : it->second;
}

и Боб должен быть твоим дядей.

В C++03:

template<class T>struct no_deduction{typedef T type;};

template<typename Key, typename T>
T convert(const Key &k, const T &d, const typename no_deduction<std::map<Key, T>>::type &m) {
  typename std::map<Key, T>::const_iterator it = m.find(k);
  return it == m.end() ? d : it->second;
}

что логически эквивалентно, но гораздо страшнее.

Как упоминает Yakk - Adam Nevraumont в своем ответе boost::assign::map_list_of не является std::map но он обратим в одного. Если вы не хотите менять свою функцию, вы можете изменить способ создания карты. С C++ у нас теперь есть список инициализатора, который можно использовать для создания объекта. Используя список инициализаторов, мы можем изменить

COLOR c = convert<MUSIC, COLOR>(COUNTRY, WHITE,
    boost::assign::map_list_of (RAP, RED) (EDM, BLUE) (ROCK, RED));

к

COLOR c = convert(COUNTRY, WHITE, {{RAP, RED},{EDM, BLUE},{ROCK, RED}});

Который даст тот же результат и позволит работать с типом шаблона.

Живой пример

Другие вопросы по тегам