std::map::size_type для std::map, у которого value_type является собственным size_type

У меня есть std::map<std::pair<std::string, std::string>, float>, который занимает слишком много памяти, и чтобы использовать меньше памяти, я решил сопоставить уникальные строки с целыми числами (например, std::map<std::string, int>, где каждая новая уникальная строка сопоставляется с текущим size() карты ) и используйте эти целочисленные значения в качестве парных ключей к карте (например, std::map<std::pair<int, int>, float>).

Вместо int я хочу использовать std::map::size_type :

using map_index = std::map::size_type;
std::pair<map_index, map_index> key;

Конечно, это не компилируется, потому что мне нужно предоставить список аргументов для карты:

vector.cc:14:19: error: invalid use of template-name `std::map' without an argument list
 using map_index = std::map::size_type;

И это (теоретически) то, чего я пытаюсь достичь:

using map_index = std::map<std::string, map_index>::size_type;

что дает следующую (ожидаемую) ошибку компилятора:

vector.cc:15:41: error: `map_index' was not declared in this scope
 using map_index = std::map<std::string, map_index>::size_type;

Как правильно заставить компилятор вывести правильный value_type для std::map, value_type которого является его собственным size_type?


person vallismortis    schedule 22.11.2018    source источник
comment
Незначительная придирка: я думаю, что вы перепутали последнее предложение. Чтобы узнать, что такое size_type, вам сначала нужно сказать, что такое value_type, а не наоборот. Когда вы знаете тип карты, получить ее size_type несложно.   -  person 463035818_is_not_a_number    schedule 22.11.2018
comment
@user463035818 user463035818 Похоже, проблема в том, что size_type является частью value_type для OP.   -  person Some programmer dude    schedule 22.11.2018
comment
У вас круговая зависимость. Почему вы не можете использовать size_t (как обычно и будет size_type)?   -  person Some programmer dude    schedule 22.11.2018
comment
можешь объяснить, зачем тебе это? afaik map::size_type - это просто псевдоним typedef всегда одного и того же типа   -  person 463035818_is_not_a_number    schedule 22.11.2018
comment
Я понимаю, что мог бы использовать size_t, или unsigned long int, или даже просто int и игнорировать предупреждения о преобразовании типов, но на самом деле я пытаюсь понять, как получить std::map::size_type для карты, которая использует результаты своего собственного std::map::size() в качестве своих значений. . Я надеюсь, что есть общее решение, основанное на шаблонах.   -  person vallismortis    schedule 22.11.2018
comment
std::map<K, V>::size_type скорее всего полностью не зависит от как K, так и V. Если вам действительно не все равно, вы можете static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")   -  person Caleth    schedule 22.11.2018
comment
Вместо использования std::map<std::string, X> будет boost::flyweight быть лучшим решением?   -  person felix    schedule 22.11.2018
comment
@felix Я из мира Java и заново знакомлюсь с C ++ через много лет, поэтому я пытался не полагаться на boost, пока не получу четкое представление о STL и его ограничениях.   -  person vallismortis    schedule 22.11.2018


Ответы (7)


То, что вы ищете, вообще говоря, невозможно.

Возможно (хотя и надуманно), что std::map<int, long>::size_type равно int, а std::map<int, int>::size_type равно long (и аналогично для других целочисленных типов), и в этом случае нет никакого способа удовлетворить std::map<int, T>::size_type как T.

И наоборот, может быть так, что std::map<int, T>::size_type определяется как T для всех T, и в этом случае не существует уникального T, удовлетворяющего вашему "требованию".

Как упоминалось в нескольких ответах (и вашей собственной справочной ссылке), на практике это вряд ли будет чем-то иным, кроме size_t.

person Max Langhof    schedule 22.11.2018
comment
Это ответ, который я искал, просто это не тот, на который я надеялся. Приведенные вами конкретные примеры прекрасно иллюстрируют проблему. - person vallismortis; 22.11.2018

size_t должно быть достаточно для такого случая.

Но если вы настаиваете, вы можете сделать так:

#include <type_traits>
#include <map>

template <class Key, class Value = size_t, size_t depth = 0, class = void>
struct GetSizeType {
    using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
};

template <class Key, class Value, size_t depth>
struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
    using type = typename std::map<Key, Value>::size_type;
};

template <class Key, class Value>
struct GetSizeType<Key, Value, 100, void> {};

int main() {
    using X = GetSizeType<int>::type;

    return 0;
}

Он будет выполняться рекурсивно на GetSizeType, рекурсивный вызов остановится на

  • достижение ограничения глубины рекурсивного вызова (в этом случае члена type не будет), или
  • найти специализацию std::map, из которых mapped_type и size_type идентичны (член type псевдонимы size_type).
person felix    schedule 22.11.2018
comment
Технический бандитский подход первого порядка - гениальный! - person Toby Speight; 22.11.2018
comment
я не полностью согласен с тем, что у вас закончится память до того, как закончится size_t. Что-то вроде my_map.size() * 2; - это разумная операция, которая может переполниться задолго до того, как у вас закончится память - person 463035818_is_not_a_number; 22.11.2018
comment
@ user463035818 Спасибо, я удалил эту часть. Я неправильно упростил использование. - person felix; 22.11.2018
comment
@ user463035818 - в контексте, заданном вопросом, арифметика не используется - результат size() просто используется как непрозрачный идентификатор. - person Toby Speight; 22.11.2018
comment
@TobySpeight Правильно, в частности, этот идентификатор всегда будет точно таким же значением, которое возвращается из size(). Я не буду вносить какие-либо изменения в эти значения до их сохранения на карте. - person vallismortis; 22.11.2018
comment
@user463035818 user463035818, я говорю, что результат size() всегда можно будет присвоить std::size_t (карта не может выйти за пределы этого диапазона), и комментарий о переполнении, если его умножить на 2, не имеет значения, потому что это не то, что делает код. . - person Toby Speight; 22.11.2018
comment
@TobySpeight ... я просто неправильно понял вопрос;) извините за путаницу - person 463035818_is_not_a_number; 22.11.2018
comment
@vallismortis Что вы подразумеваете под более крайними случаями? Если вы используете std::size_t, он ни за что не будет работать там, где будет работать другой тип, потому что он должен иметь возможность обращаться даже к объектам размера 1. - person Acorn; 22.11.2018
comment
Не могли бы вы доказать сходимость этого алгоритма? Реализация может сделать его круговым: map‹...,size_t›::size_type — это int, а map‹…,int›::size_type — это size_t. - person Oliv; 22.11.2018
comment
@ Олив Ты прав. Алгоритм должен остановиться. Я добавляю к нему ограничение глубины рекурсии. Не следовало полагаться на проверку глубины компилятором. - person felix; 22.11.2018

Отказ от ответственности: это решение довольно глупое. Мы просто собираемся решить уравнение, многократно (обычно один раз) пытаясь создать экземпляр std::map, пока не найдем тот, у которого есть запрошенный ключ и его собственный size_type в качестве значения.

template <class T>
struct identity {
    using type = T;
};

template <class K, class V = char>
struct auto_map {
    using map_type = std::map<K, V>;
    using type = typename std::conditional_t<
        std::is_same_v<
            typename map_type::mapped_type,
            typename map_type::size_type
        >,
        identity<map_type>,
        auto_map<K, typename map_type::size_type>
    >::type;
};

template <class K>
using auto_map_t = typename auto_map<K>::type;

Если метафункция не может найти такую ​​карту, она либо выдаст ошибку, потому что type в конечном итоге определена сама для себя, либо нарушит ограничение рекурсии.

person Quentin    schedule 22.11.2018
comment
Оба показанных рекурсивных решения круты, но я не уверен, зачем их использовать. Это все еще не гарантирует, что вы найдете тот, который работает в сломанной среде, как вы говорите, поэтому чище и быстрее компилировать, чтобы просто выбрать один и подтвердить условие, которое нам действительно нужно, не так ли? - person Acorn; 22.11.2018
comment
@ Желудь да. Мне просто нужно было чем-то заняться, пока я делаю полную перестройку;) - person Quentin; 22.11.2018

Используйте 1_. Целое число без знака std::map::size_type не будет больше std::size_t и на практике будет того же типа.

Если вы хотите быть уверенным, заявите об этом:

static_assert(std::is_same_v<
    std::size_t,
    std::map<std::string, std::size_t>::size_type
>);
person Acorn    schedule 22.11.2018

Все реализации C++ в дикой природе, которые я использовал, используют один и тот же тип размера для всех карт.

So;

using map_size_type = std::map<int, int>::size_type;
using my_map = std::map<std::string, map_size_type>;
static_assert(std::is_same<map_size_type, my_map::size_type);

это просто вызывает ошибку компиляции, если (разумное) предположение не выполняется.

person Yakk - Adam Nevraumont    schedule 22.11.2018
comment
Я ссылаюсь на другой вопрос здесь, потому что ваш ответ также имеет отношение к нему. - person vallismortis; 22.11.2018

Единственный способ разорвать круговую зависимость — использовать определенный тип. Я рекомендую вам просто сделать map_index std::size_t - C++ строго подразумевает, что std::size_t будет назначен на map::size_type.

person Toby Speight    schedule 22.11.2018

Но вы уверены, что size_type из std::map зависит от типов ключ/значение?

Если да, то я не вижу способа получить его.

Но size_type не должен зависеть от типов ключ/значение и обычно равен std::size_t.

Я предлагаю

using Index0 = typename std::map<std::string, std::size_t>::size_type;

using mapIndex = typename std::map<std::string, Index0>::size_type;    

Вы можете проверить, что вы получили правильный тип с помощью

static_assert( std::is_same_v<Index0, mapIndex>, "no right type");
person max66    schedule 22.11.2018
comment
У компилятора нет возможности узнать, что тип размера карты всегда один и тот же — что касается его, вполне может существовать другая специализация. - person Toby Speight; 22.11.2018
comment
@TobySpeight Да, именно это побудило меня задать этот вопрос. Это обычно то же самое, что и предложение size_t из документации карты C++, которая действительно привлекла внимание. я думаю об этом. - person vallismortis; 22.11.2018
comment
@vallismortis - с теоретической точки зрения вы правы (насколько мне известно). Я не вижу способа разорвать циклическую зависимость, но добавив static_assert( std::is_same_v<Index0, mapIndex>, "no right type");, вы можете проверить, что выбран правильный тип. - person max66; 22.11.2018
comment
@TobySpeight - ты прав (насколько мне известно); но через static_assert() (см. мой измененный ответ) мы можем проверить, получили ли мы правильный тип. - person max66; 22.11.2018