А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора?
Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.
Здравствуйте, Alexander G, Вы писали:
AG>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора? AG>Ну, т.е. написать какой-нибуь char вместо типа?
Формально это запрещено. И есть примеры, когда из-за этого возникают проблемы. Следовательно, так делать не надо. Смотри дальше, например, обсуждение.
Здравствуйте, Alexander G, Вы писали:
AG>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора? AG>Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.
Ну, если рассуждать в подобном ключе далее, то вся система типов — "не нужна"TM, ибо вполне можно жить на "void*". Но зачем стрелять себе ногой в верёвку?
Здравствуйте, Alexander G, Вы писали:
AG>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора? AG>Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.
// выковыриваем тип элемента из мапаtemplate<class K, class V>
using map_item = typename std::map<K,V>::value_type;
// либо из стандарта
= std::pair<K const, V>;
// пишем шаблон мапа, явно параметризуемый шаблоном аллокатора
// (поэтому он идёт первым параметром, а не четвёртым дефолтным после компаратора)template<template<class> class A, class K, class V, class C = std::less<K>>
using customized_map = std::map<K, V, C, A<map_item<K,V>>>;
// произвольных аллокаторов под рукой не оказалось, взял готовый стандартный :) template<class E> usingmy_allocator = std::allocator<E>;
// делаем на его основе шаблон "нашего" мапаtemplate<class K, class V, class C = std::less<K>>
usingmy_map = customized_map<my_allocator, K, V, C>;
// хотя можно всё и в одну строчку было записатьtemplate<class K, class V, class C = std::less<K>>
usingmy_map = std::map<K, V, C, my_allocator<std::pair<K const, V>>>;
// и пользуемся на здоровьеusing the_map = my_map<int,char>;
using eth_map = my_map<int,char,std::greater<int>>;
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, Alexander G, Вы писали:
AG>>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора? AG>>Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.
MD>Ну, если рассуждать в подобном ключе далее, то вся система типов — "не нужна"TM, ибо вполне можно жить на "void*".
Не надо передёргивать.
Изначальный интерфейс аллокаторов в С++ очень плох. И устарел.
В нём все аллокаторы одного типа должны были быть эквивалентны друг-другу, то есть гарантированно работать со стандартными контейнерами могли лишь stateless аллокаторы. Хочешь завести в программе пару пулов строчек? Перебьёшься! То есть даже нельзя надёжно и эффективно написать с независимыми аренами
Ибо по стандарту строку, которую выделил аллокатор my_arena_allocator<string>(arena1), должен уметь разрушать любой другой экземпляр, например my_arena_allocator<string>(arena2) или даже my_arena_allocator<string>(NULL), что в общем-то никак не способствует эффективности. В результате чего нужно либо отказываться от std::vector (и прочих контейнеров STL), либо связывать все экземпляры аллокаторов между собой, чтобы один аллокатор мог всегда найти другой экземпляр, который изначально создал объект, и передать ему его для уничтожения (подобно тому, как это сделано в boost::fast_pool_allocator с синхронизацией через mutex), — и это ужасно.
Но зато интерфейс аллокаторов позволяет поддерживать одновременно near и far pointers. Ну очень востребованная возможность в современном программировании. Особенно с учётом того, что как оказалось, этой возможностью всё равно не получилось полноценно пользоваться: сам Степанов в интервью приводит пример почему этот механизм не работает.
Вот что важнее, быстрый и эффективный memory_pool или обязательная поддержка устаревших/редких моделей памяти даже для тех, кто ими не пользуется?
К счастью в C++11 аллокаторы всё же были переработаны. Пусть и и не все многочисленные предложения удалось воплотить. Но хотя бы stateful-аллокаторы теперь разрешены в стандартных контейнерах!
И изначальный вопрос в целом правильный. Точно также можно построить реализацию контейнеров, в которой узаконить обязательный rebind аллокатора, и тогда указывать типы вручную не понадобится — и код станет короче (может быть даже выразительнее, если синтаксический сахар в виде std::allocator<auto> до кучи добавить). Наверное, в некоторых хитрых местах, завязанных на не-плоскую память, может что-то поломаться и понадобится что-то дописать — там код станет длиннее. Кажется, что в среднем было бы выгоднее иметь обязательный rebind и более лаконичный код. И даже если кому-то понадобится писать полный и точный тип, то эта возможность у них останется. Сейчас же типы выписывать вынуждены все.
Так что хоть формальный запрет и есть, но у него нет никакого сакрального смысла — это просто ещё одно из неудачных решений ранних версий языка, которое, к сожалению, уже нельзя легко изменить.
Здравствуйте, Mr.Delphist, Вы писали:
MD>Ну, если рассуждать в подобном ключе далее, то вся система типов — "не нужна"TM, ибо вполне можно жить на "void*". Но зачем стрелять себе ногой в верёвку?
Ну это же давно известный баг дизайна, вызванный нищетой древних компиляторов. (Хотя по-настоящему от этой нищеты избавились только в С++14).
Просто в стандарте он гвоздями приколочен, и не дай бог, кто-то где-то будет полагаться на него.
A propos. Ребинд — это вообще прекрасно: это же теоркатовский функтор. Интересно, Степанов курил теоркат специально, или ему муза нашептала, или оно само получилось?
Здравствуйте, Кодт, Вы писали:
К>Можно упростить себе жизнь с помощью шаблонных подстановок
Можно ещё и template template parameters запользовать, благо type alias теперь есть (чьё отсутсвие, наверное, и сыграло в пользу существующего интерфейса аллокаторов).
#include <iostream>
#include <map>
#include <functional>
template<class K, class V> using map_item = typename std::map<K, V>::value_type;
template<class K, class V, template<class> class C = std::less, template<class> class A = std::allocator>
using my_map = std::map<K, V, C<K>, A<map_item<K,V>>>;
template<class E> using my_allocator = std::allocator<E>;
using the_map = my_map<int, char>;
using eth_map = my_map<int, char, std::greater, my_allocator>;
int main() {
eth_map m;
m[1] = 2;
return 0;
}
Здравствуйте, Кодт, Вы писали:
К>Можно упростить себе жизнь с помощью шаблонных подстановок К>http://ideone.com/fVP458
Годится.
Ещё бы multimap, unordered_map и list (на нём не ощущалась проблема, но тоже используется), и тогда логично всех в один traits-подобный класс:
#include <iostream>
#include <map>
#include <list>
template<template<class> class A>
struct customized_allocator
{
template<class K, class V, class C = std::less<K>>
using map = std::map<K, V, C, A<std::pair<K const, V>>>;
template<class K, class V, class C = std::less<K>>
using multimap = std::multimap<K, V, C, A<std::pair<K const, V>>>;
template<class K, class V>
using list = std::list<V, A<V>>;
};
template<class E> using my_allocator = std::allocator<E>;
using use_my_allocator = customized_allocator<my_allocator>;
using the_map = use_my_allocator::map<int,char>;
using eth_map = use_my_allocator::map<int,char,std::greater<int>>;
Здравствуйте, Alexander G, Вы писали:
AG>Ещё бы multimap, unordered_map и list (на нём не ощущалась проблема, но тоже используется), и тогда логично всех в один traits-подобный класс:
Осталось только нормальное имя для фабрики типов дать, чтобы не путать фабрику с аллокатором. use_customized_allocator, with_customized_allocator, и т.п...