std::allocator_traits по подразбиране с разпределител, който има повече от един параметър на шаблона

std::allocator_traits върши своята магия автоматично, когато предоставя контейнер в стил STL с разпределител, който има един параметър на шаблон, но не и когато предоставя контейнер в стил STL с разпределител, който има два параметъра на шаблон, но иначе е подобен.

Какво трябва да направя, за да кажа на std::allocator_traits как да взаимодейства с разпределител, който има повече от един параметър на шаблон? Възможно ли е да накарате std::allocator_traits да предостави разумни стойности по подразбиране в този случай?

Като пример, ако взема простия разпределител, който Хауърд Хинант предоставя в Allocator Boilerplate, и го подавам към std::vector<> тогава всичко е наред. Ако добавя фиктивен параметър int към шаблона allocator (и направя леки модификации, ако е необходимо), тогава получавам грешки на компилатора, защото компилаторът не можа да намери rebind, наред с други неща.

Ето това описание в код:

http://coliru.stacked-crooked.com/a/173c57264137a351

Ако трябва сам да специализирам std::allocator_traits в този случай, има ли начин все пак да получа настройките по подразбиране?


person Praxeolitic    schedule 18.01.2016    source източник
comment
@bolov Все пак искам std::allocator_traits да попълни повечето подробности вместо мен. Има ли някакво предимство за специализиране на std::allocator_traits срещу завършване на концепцията за разпределител без него?   -  person Praxeolitic    schedule 18.01.2016
comment
@bolov това не се препоръчва, просто предоставете свой собствен rebind в разпределителя, вижте моя отговор.   -  person TemplateRex    schedule 18.01.2016
comment
Започнете да декоментирате някои от нещата в Allocator Boilerplate, за да коригирате грешките по време на компилиране. Разбира се, ще трябва да направите нещо повече от разкоментиране, ще трябва да накарате каквото и да правите да разкоментирате в съответствие с вашия разпределител. Започнете с rebind. Това може да е единственото друго нещо, което трябва да не по подразбиране, което би било много по-лесно от специализирането на allocator_traits.   -  person Howard Hinnant    schedule 19.01.2016
comment
@HowardHinnant любопитно, libc++ компилира с примера на OP, докато libstdc++ не го прави. Знаете ли/помните ли какво прави libc++ за разпределители с нетипови допълнителни параметри (хвърлих поглед към източника, но изглеждаше, че се съпоставя само обикновеният Alloc<T, Args...>).   -  person TemplateRex    schedule 19.01.2016
comment
@TemplateRex: Най-доброто ми предположение е, че vector на libc++ не си прави труда да извиква rebind. Примерът на OP вероятно ще се провали с друг контейнер, който трябва да извика rebind.   -  person Howard Hinnant    schedule 19.01.2016
comment
@HowardHinnant току-що провери, с std::list вместо std::vector, libc++ също не се компилира. Вижте също този Въпрос и отговор, където @JonathanWakely обяснява практиката на libstdc++ винаги да преминава през rebind.   -  person TemplateRex    schedule 19.01.2016
comment
@TemplateRex: Да, libc++ активно отхвърля идеята, че приемането/компилирането на vector<int, std::allocator<char>> прави услуга на клиента. libc++ прави всичко възможно да уведоми клиента възможно най-скоро, ако има несъответствие като това между разпределителя и контейнера: github.com/llvm-mirror/libcxx/blob/master/include/ Мотивацията за това поведение е, че логиката на програмата може да зависи от два независимо декларирани контейнера, имащи същия тип. И ако техният тип се различава само от случайно неправилно декларирани разпределители, най-добре е да знаете по време на компилация, възможно най-скоро.   -  person Howard Hinnant    schedule 19.01.2016
comment
Fwiw, научих този урок преди години, докато разработвах CodeWarrior std::lib.   -  person Howard Hinnant    schedule 19.01.2016


Отговори (1)


Стандартът предоставя само rebind по подразбиране за разпределители с множество параметри на тип на шаблона:

17.6.3.5 Изисквания към разпределителя [allocator.requirements]

3 Бележка A: Шаблонът на членски клас rebind в таблицата по-горе е ефективно шаблон с typedef. [ Забележка: Като цяло, ако името Allocator е обвързано с SomeAllocator<T>, тогава Allocator::rebind<U>::other е от същия тип като SomeAllocator<U>, където SomeAllocator<T>::value_type е T, а SomeAllocator<U>:: value_type е U. — крайна бележка ] Ако Allocator е екземпляр на шаблон на клас на формата SomeAllocator<T, Args>, където Args е нула или повече аргументи за тип, а Allocator не предоставя rebind шаблон на член, стандартният шаблон allocator_traits използва SomeAllocator<U, Args> > в място на Allocator:: rebind<U>::other по подразбиране. За типове разпределители, които не са шаблонни екземпляри на горния формуляр, не се предоставя по подразбиране.

Тъй като имате параметър без тип (int), не е осигурено по подразбиране. Поправката е проста: просто добавете свое собствено повторно свързване към вашия разпределител.

template<class T, int I>
class allocator_w_int
{
    // as before

    template<class U>
    struct rebind { using other = allocator_w_int<U, I>; };    
};

Пример на живо

Що се отнася до обосновката на допускането на разпределители на формата Allocator<T, Args...>, но не и на разпределители на формата Alloc<T, Ns...>, може само да се гадае, но тогава това също би довело до изобилието от Alloc<T, Args.., Ns...> и т.н. и т.н. Ето защо библиотеките за метапрограмиране на шаблони ( като Boost.MPL) винаги обвиват своите параметри без тип N от тип T в неща като integral_constant<T, N>. Това също би бил маршрут за вас, като дефинирате

template<class T, class Arg>
class allocator_w_int; // leave undefined

template<int N>
using int_ = std::integral_constant<int, N>;

template<class T, int I>
class allocator_w_int<T, int_<I>>
{
    // replace all occurances of I, J --> int_<I>, int_<J>
};

Пример на живо

person TemplateRex    schedule 18.01.2016