map::const_iterator tipo mapeado não é const

Sep 02 2020

Estou tentando escrever um adaptador iterador para uma classe construída em cima de um mapa. Estou tendo problemas ao tentar obter o tipo mapeado do tipo de iterador. Basicamente eu gostaria de obter:

  • map<Key,Val>::iterator --> Val
  • map<Key,Val>::const_iterator --> Val const

Aqui está uma reprodução mínima.

#include <map>
#include <type_traits>
    
template <typename BaseIteratorT>
using ValueType = typename BaseIteratorT::value_type::second_type;
    
// Passes
static_assert(
    std::is_same<ValueType<std::map<double, int>::iterator>, int>::value,
    "bad type for mutable iterator");

// Fails
static_assert(
    std::is_same<ValueType<std::map<double, int>::const_iterator>, const int>::value,
    "bad type for const iterator");

Como posso conseguir isso (C++14)?

Respostas

2 PasserBy Sep 02 2020 at 08:18

Para quase todos os propósitos práticos, os tipos que você pode querer deduzir estão vinculados a alguma expressão, e nesse caso decltypeé sempre a resposta

template<typename T>
using mapped_type = std::remove_reference_t<decltype((std::declval<T>()->second))>;

using m = std::map<char, int>;
using i = mapped_type<m::iterator>  // int
using ci = mapped_type<m::const_iterator>  // const int
2 RemyLebeau Sep 02 2020 at 07:54

O value_typede um std::mapé std::pair<const Key, T>. O second_typedesse par é apenas T, não const T, não const T&, apenas T. Desreferenciar a map::const_iteratorretornará uma const pair&referência, mas isso não mudará o par second_typepara const. Se você quiser isso, detecte se o iterador value_typeé constou não, como with std::is_const, e se sim, aplique constao seu second_type, como with std::add_const.