Шаблон функции и неоднозначный параметр шаблона - PullRequest
3 голосов
/ 01 октября 2019

У меня есть несколько std::map<some_type, some_other_type>, и я пытаюсь написать шаблон функции Lookup, как показано ниже.

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

#include <iostream>
#include <map>

// Usage :
//   bool valueisinmap = Lookup(themap, thekey, thevalue);
template <typename TK, typename TV>
bool Lookup(std::map<TK, TV>& map,  TK key, TV& value)
{
  auto it = map.find(key);
  if (it != map.end())
  {
    value = it->second;
    return true;
  }
  else
  {
    return false;
  }
}

int main()
{
    std::map<std::string, std::string> m;
    m.insert(std::make_pair("2", "two"));

    std::string x;
    std::string key = "2";

    if (Lookup(m, key, x))
      std::cout << "OK\n";

    if (Lookup(m, "2", x))  // problem here
      std::cout << "OK\n";
}

Я понимаю, почему Lookup(m, "2", x) не делаетt скомпилировать, потому что тип "2" не std::string, но есть способ написать шаблон функции, чтобы я мог использовать Lookup(m, "2", x), а также Lookup(m, key, x), key как std::string?

И если да, то возникает второй вопрос:

bool Lookup(std::map<TK, TV>& map,  TK key, TV& value)

key передается по значению, и если тип key равен std::string, создается копия. Есть ли способ передать key по ссылке (или какой-нибудь магии C ++ 14 и плюс) и при этом использовать Lookup(m, "2", x)?

Ответы [ 3 ]

3 голосов
/ 01 октября 2019

Вы можете ввести другой параметр шаблона для key, когда @ Тон ван ден Хеувель ответил , другой способ - исключить его из вывода аргумента шаблона :

template <typename TK, typename TV>
bool Lookup(std::map<TK, TV>& map, const std::type_identity_t<TK>& key, TV& value)

Тогда TK будет выведено только из 1-го параметра map;если вы передадите const char[] функции как key, она будет преобразована в std::string, а затем передана в качестве аргумента. И вы можете сделать это путем передачи по константной ссылке, чтобы избежать потенциальной ненужной копии.

LIVE

КСТАТИ: std::type_identity поддерживается с C ++ 20;если ваш компилятор не поддерживает его, вы можете легко создать свой собственный.

template<typename T> struct type_identity { typedef T type; };
3 голосов
/ 01 октября 2019

Одним из способов решения этой проблемы является введение отдельного параметра типа для типа ключа следующим образом:

template <typename TKM, typename TK, typename TV>
bool Lookup(const std::map<TKM, TV>& map, const TK& key, TV& value)
{
  auto it = map.find(key);
  if (it != map.end())
  {
    value = it->second;
    return true;
  }
  else
  {
    return false;
  }
}

Пока TK неявно преобразуется в TKM, Lookup можетвызываться с ключом типа TK в сочетании с картой с типом ключа TKM.

1 голос
/ 01 октября 2019

Вам нужны две вещи здесь. Во-первых, тип ключа может быть определен отдельно (typename K ниже). Во-вторых, вы хотите передать ключ как const -качественную ссылку и настроить карту с прозрачной функцией сравнения C ++ 14 (typename Comp ниже), чтобы избежать ненужных копий (подробности см. в этой теме )о прозрачных компараторах).

template <typename TK, typename TV, typename Comp, typename K>
bool Lookup(const std::map<TK, TV, Comp>& map,  const K& key, TV& value)
{
   // Same as before...
}

std::map<std::string, std::string, std::less<>> m;

Указание std::less<> в качестве типа сравнения std::map обеспечивает доступность перегрузок № 3 и № 4 std::map::find.

Обратите внимание, что я дополнительно const -квалифицировал сам параметр карты, поскольку шаблон Lookup не изменяет его.

...