Как остановить автоматическое преобразование из int в float и наоборот в std:: map
Я написал небольшую программу использования std:: map здесь следующим образом.
int main()
{
map<int,float>m1;
m1.insert(pair<int,float>(10,15.0)); //step-1
m1.insert(pair<float,int>(12.0,13)); //step-2
cout<<"map size="<<m1.size()<<endl; //step -3
Я создал карту с типом типа int в качестве ключа и типа float в качестве пары значений (ключ-значение) для карты m1
step-1 Создал нормальную пару с int-float и вставил в карту.
step-2 Создал пару cross float-int и вставлен в карту. Теперь я знаю, что неявное преобразование делает эту пару вставленной в карту.
Здесь я просто не хочу, чтобы неявное преобразование выполнялось, и должна быть указана ошибка компилятора.
Какие изменения я должен сделать в этой программе/карте, чтобы сделать флаг comipiler ошибкой, когда мы пытаемся выполнить операцию типа 2?
Спасибо заранее.
Ответы
Ответ 1
Вот предложение:
template <typename K, typename V, typename W>
void map_insert(map<K,V>& m, K k, W w) {
V v = w;
m.insert(pair<K,V>(k,v));
}
int main() {
map<int,float>m1;
map_insert(m1, 10, 15.0);
map_insert(m1, 12.0, 13); // compiler complains here
cout<<"map size="<<m1.size()<<endl;
Третий параметр шаблона немного неудобен, но необходимо, чтобы кастинг от double
до float
.
Ответ 2
Это невозможно (и даже если это возможно, тогда это будет основной хак, который вы не должны использовать).
insert
принимает аргумент value_type
, который является pair<int const,float>
.
Итак, когда вы пытаетесь вставить pair<float, int>
, компилятор ищет преобразование, то есть: конструктор pair<int const, float>
, который принимает аргумент pair<float, int>
, который просто существует. Фактически, я попытался придумать частичную специализацию для этого члена шаблона (который позволяет преобразовать), который тогда вы могли бы сбой по оставшемуся параметру шаблона, но я этого не сделал; это кажется невозможным. Во всяком случае, это был бы очень грязный хак, который вы просто не должны делать, чтобы избежать опечатки. В другом месте вам может понадобиться это преобразование, и в любом случае нет никакого определения в пространстве имен std.
Итак, что такое решение "Как я могу избежать такого рода опечаток?"
Вот что я обычно делаю:
1) Все мои карты имеют typedef для их типа.
2) Затем я использую ::value_type
(и ::iterator
и т.д.) Только для этого типа.
Это не только более надежное, но и более гибкое: вы можете позже изменить тип контейнера и, скорее всего, код будет работать.
Итак, ваш код будет выглядеть следующим образом:
int main()
{
typedef std::map<int,float> m_type;
m_type m1;
m1.insert(m_type::value_type(10,15.0)); // allowed
m1.insert(m_type::value_type(12.0,13)); // no risk for a typo.
Альтернативным решением будет обернуть ваш float
в пользовательский класс. В любом случае, это не так уж плохо, потому что (опять) причины гибкости. Очень редко приходится писать код с помощью std::map<int, builtin-type>
, чтобы понять, что вам нужно хранить больше данных, и, поверьте, это происходит очень часто. Вы могли бы начать с класса с самого начала.
Ответ 3
Может быть, есть более простой способ, но это то, что произошло со мной:
#include <iostream>
#include <map>
template<typename Key, typename Value>
struct typesafe_pair
{
const Key& key;
const Value& value;
explicit typesafe_pair(const Key& key, const Value& value): key(key), value(value) {}
operator typename std::map<Key, Value>::value_type() { return typename std::map<Key, Value>::value_type(key, value); }
};
int main()
{
std::map<int,float>m1;
m1.insert(std::pair<int,float>(10,15.0)); // allowed
m1.insert(std::pair<float,int>(12.0,13)); // allowed!!
m1.insert(typesafe_pair<int,float>(10, 15.0)); // allowed
m1.insert(typesafe_pair<float, int>(12.0, 13)); // compiler error
std::cout << "map size=" << m1.size() << std::endl; //step -3
}
РЕДАКТИРОВАТЬ: 1 Кто-то может предоставить лучшее (более эффективное) решение, включающее ссылки на rvalue и совершенную магию переадресации, которую я пока не совсем понимаю.
EDIT 2: Я думаю, что Carlo Wood имеет лучшее решение IMHO.