Why is template inference not working?

The following toy program will convert the music type to the appropriate color. He compiles and runs just fine - the conversion COUNTRY

fails, as expected, and the function conversion()

returns the default value WHITE

. However, if I remove the template arguments, the template argument <MUSIC, COLOR>

deduction does not recognize which types to use. How can I make deduction work?

#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;
}

      

+3


source to share


2 answers


boost::assign::map_list_of

is probably not a type map<K,V>

, but rather some type being converted to it.

The compiler tries to infer the type from the first two arguments, and the last one is 1. The last one is meaningless, so it refuses.

We can block output on the last argument as follows:

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;
}

      



and bob should be your uncle.

In 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;
}

      

which is logically equivalent, but much uglier.

+6


source


As Yakk mentions in his answer boost::assign::map_list_of

not std::map

, but it is a convertible into one. If you don't want to change your function, you can change the way the map is created. With C ++, we now have a list of initializers that can be used to construct an object. Using the initializer list, we can change

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

      

For



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

      

Which will give the same result and allow the template type to be subtracted.

Live example

+3


source







All Articles