Sono riuscito a ottenere una prima bozza piuttosto solida di rebind in corso. Funziona con tutti i contenitori STL (tranne le combinazioni meno comuni di parametri modello), gli adattatori contenitore e std::integer_sequence
. E probabilmente funziona anche per molte altre cose. Ma certamente non funzionerà per tutto.
Il guaio principale era far sì che i tipi di mappa funzionassero come previsto da Yakk, ma un piccolo tratto di carattere ha contribuito a questo.
così via per il codice ...
void_t
template<class...>
using void_t = void;
Questo piccolo trucco di Walter E. Brown rende tipo attuazione Tratti molto più facile.
caratteri morfologici
template<class T, class = void>
struct is_map_like : std::false_type {};
template<template<class...> class C, class First, class Second, class... Others>
struct is_map_like<C<First, Second, Others...>,
std::enable_if_t<std::is_same<typename C<First, Second, Others...>::value_type::first_type,
std::add_const_t<First>>{} &&
std::is_same<typename C<First, Second, Others...>::value_type::second_type,
Second>{}>>
: std::true_type {};
template<class T, class U, class = void>
struct has_mem_rebind : std::false_type {};
template<class T, class U>
struct has_mem_rebind<T, U, void_t<typename T::template rebind<U>>> : std::true_type {};
template<class T>
struct is_template_instantiation : std::false_type {};
template<template<class...> class C, class... Others>
struct is_template_instantiation<C<Others...>> : std::true_type {};
is_map_like
usa il fatto che la mappa-come tipi nella STL tutti hanno value_type
definito per essere un (n) std::pair
con il const
Ed primo parametro modello del tipo mappa, essendo lo first_type
nello pair
. Il secondo parametro template del tipo map-like corrisponde esattamente allo second_type
dello pair
. rebind
deve gestire con maggiore attenzione i tipi di mappa.
has_mem_rebind
rileva la presenza di un membro rebind
meta-funzione su T
utilizzando il trucco void_t
. Se una classe ha rebind
, prima rimanderemo all'implementazione delle classi.
is_template_instantiation
rileva se il tipo T
è un'istanza di modello. Questo è più per il debug.
Helper Tipo Lista
template<class... Types>
struct pack
{
template<class T, class U>
using replace = pack<
std::conditional_t<
std::is_same<Types, T>{},
U,
Types
>...
>;
template<class T, class U>
using replace_or_rebind = pack<
std::conditional_t<
std::is_same<Types, T>{},
U,
typename rebind<Types, U>::type
>...
>;
template<class Not, class T, class U>
using replace_or_rebind_if_not = pack<
std::conditional_t<
std::is_same<Types, Not>{},
Types,
std::conditional_t<
std::is_same<Types, T>{},
U,
typename rebind<Types, U>::type
>
>...
>;
template<class T>
using push_front = pack<T, Types...>;
};
Questo gestisce alcune elenco semplice come manipolazioni di tipo
replace
sostituisce tutte le occorrenze di T
con U
in modo non ricorsivo.
replace_or_rebind
sostituisce tutte le occorrenze di T
con U
, e per tutte le occorrenze non corrispondenti, chiamate Rebind
replace_or_rebind_if_not
è uguale replace_or_rebind
ma salta su ogni elemento corrispondente Not
push_front
spinge semplicemente un elemento al fronte del tipo list
Calling Stati Rebind
// has member rebind implemented as alias
template<class T, class U, class = void>
struct do_mem_rebind
{
using type = typename T::template rebind<U>;
};
// has member rebind implemented as rebind::other
template<class T, class U>
struct do_mem_rebind<T, U, void_t<typename T::template rebind<U>::other>>
{
using type = typename T::template rebind<U>::other;
};
Si scopre che esistono due diversi modi validi per implementare un membro rebind
in base allo standard. Per allocators è rebind<T>::other
. Per pointers è solo rebind<T>
. Questa implementazione di do_mem_rebind
va con rebind<T>::other
se esiste, altrimenti torna al più semplice rebind<T>
.
disimballaggio
template<template<class...> class C, class Pack>
struct unpack;
template<template<class...> class C, class... Args>
struct unpack<C, pack<Args...>> { using type = C<Args...>; };
template<template<class...> class C, class Pack>
using unpack_t = typename unpack<C, Pack>::type;
questo richiede un pack
, estrae i tipi in esso contenuti, e li mette in qualche altro modello C
.
Rebind Attuazione
La roba buona.
template<class T, class U, bool = is_map_like<T>{}, bool = std::is_lvalue_reference<T>{}, bool = std::is_rvalue_reference<T>{}, bool = has_mem_rebind<T, U>{}>
struct rebind_impl
{
static_assert(!is_template_instantiation<T>{}, "Sorry. Rebind is not completely implemented.");
using type = T;
};
// map-like container
template<class U, template<class...> class C, class First, class Second, class... Others>
class rebind_impl<C<First, Second, Others...>, U, true, false, false, false>
{
using container_type = C<First, Second, Others...>;
using value_type = typename container_type::value_type;
using old_alloc_type = typename container_type::allocator_type;
using other_replaced = typename pack<Others...>::template replace_or_rebind_if_not<old_alloc_type, First, typename U::first_type>;
using new_alloc_type = typename std::allocator_traits<old_alloc_type>::template rebind_alloc<std::pair<std::add_const_t<typename U::first_type>, typename U::second_type>>;
using replaced = typename other_replaced::template replace<old_alloc_type, new_alloc_type>;
using tail = typename replaced::template push_front<typename U::second_type>;
public:
using type = unpack_t<C, typename tail::template push_front<typename U::first_type>>;
};
// has member rebind
template<class T, class U>
struct rebind_impl<T, U, false, false, false, true>
{
using type = typename do_mem_rebind<T, U>::type;
};
// has nothing, try rebind anyway
template<template<class...> class C, class T, class U, class... Others>
class rebind_impl<C<T, Others...>, U, false, false, false, false>
{
using tail = typename pack<Others...>::template replace_or_rebind<T, U>;
public:
using type = unpack_t<C, typename tail::template push_front<U>>;
};
// has nothing, try rebind anyway, including casting NonType template parameters
template<class T, template<class, T...> class C, class U, T FirstNonType, T... Others>
struct rebind_impl<C<T, FirstNonType, Others...>, U, false, false, false, false>
{
using type = C<U, U(FirstNonType), U(Others)...>;
};
// array takes a non-type parameter parameters
template<class T, class U, std::size_t Size>
struct rebind_impl<std::array<T, Size>, U, false, false, false, false>
{
using type = std::array<U, Size>;
};
// pointer
template<class T, class U>
struct rebind_impl<T*, U, false, false, false, false>
{
using type = typename std::pointer_traits<T*>::template rebind<U>;
};
// c-array
template<class T, std::size_t Size, class U>
struct rebind_impl<T[Size], U, false, false, false, false>
{
using type = U[Size];
};
// c-array2
template<class T, class U>
struct rebind_impl<T[], U, false, false, false, false>
{
using type = U[];
};
// lvalue ref
template<class T, class U>
struct rebind_impl<T, U, false, true, false, false>
{
using type = std::add_lvalue_reference_t<std::remove_reference_t<U>>;
};
// rvalue ref
template<class T, class U>
struct rebind_impl<T, U, false, false, true, false>
{
using type = std::add_rvalue_reference_t<std::remove_reference_t<U>>;
};
- Il caso sicuro per
rebind
è semplicemente lasciare invariato il tipo. Ciò consente di chiamare rebind<Types, double>...
senza doversi preoccupare se ogni Type
in Types
è in grado di supportare rebind
. C'è un static_assert
lì nel caso in cui riceve un modello di istanziazione. Se questo è successo, probabilmente hai bisogno di un'altra specializzazione di rebind
- La mappa come
rebind
si aspetta di essere invocata come rebind<std::map<int, int>, std::pair<double, std::string>>
. Quindi il tipo a cui si sta eseguendo il rimpiazzo dell' allocatore non corrisponde esattamente al tipo di rimbalzo del contenitore. Fa un replace_or_rebind_if_not
su tutti i tipi tranne i tipi Key e Value, con if_not
allocator_type
. Poiché il tipo di allocatore differisce dalla coppia chiave/valore rebind
, è necessario modificare il valore const
del primo elemento della coppia. Utilizza std::allocator_traits
per riassociare l'allocatore, poiché tutti gli allocatori devono essere riconciliabili tramite std::allocator_traits
.
- Se
T
ha un membro rebind
, utilizzare quello.
- Se
T
non ha alcun membro rebind
, replace_or_rebind
tutti i parametri del modello C
corrispondenti al primo parametro del modello C
.
- Se
T
ha un parametro di tipo e un gruppo di parametri di modello non di tipo il cui tipo corrisponde a tale parametro. Tentativo di ripetere tutti questi parametri non di tipo su U
. Questo è il caso che fa funzionare std::integer_sequence
.
- Un caso speciale è stato richiesto per
std::array
poiché accetta un parametro di modello non di tipo che fornisce le sue dimensioni e tale parametro del modello deve essere lasciato in bianco.
- Questo caso consente di riaffilare i puntatori ad altri tipi di puntatore. Utilizza
std::pointer_traits
's rebind
per realizzare questo.
- Lets
rebind
lavoro sul c-array di dimensioni ex: T[5]
- Lets
rebind
lavoro sul c-array senza una dimensione ex: T[]
rebind
s lvalue-ref T
tipi ad un lvalue-ref garantito per std::remove_reference_t<U>
.
rebind
s rvalue-ref T
tipi a un valore garantito-ref a std::remove_reference_t<U>
.
Derivato (Exposed) Classe
template<class T, class U>
struct rebind : details::rebind_impl<T, U> {};
template<class T, class U>
using rebind_t = typename rebind<T, U>::type;
Back To SFINAE e static_assert
Dopo molto googling non sembra essere un modo generico per SFINAE intorno static_assert
s come quelli contenuti nei contenitori STL di libC++. Mi fa davvero desiderare che la lingua abbia qualcosa di più amichevole di SFINAE, ma un po 'più ad-hoc dei concetti.
come:
template<class T>
static_assert(CACHE_LINE_SIZE == 64, "")
struct my_struct { ... };
La tua domanda non è chiara come l'esempio si dà non avrebbe mai funzionato. Intendi 'sample_rebind, int>'? E anche quello risulterebbe in 'fake_cont >' se si mantiene 'OtherArgs ...'. Allo stato attuale, il compilatore è semplicemente corretto per rifiutare il codice. –
Ho commentato 'static_assert' in' fake_cont', e in effetti compila (almeno su clang 3.5), passando il static_assert finale. Non sto dicendo che il compilatore è sbagliato, sto solo chiedendo come posso fare in modo che il compilatore non fallisca nella parte 'is_constructible' della specializzazione. Nota che se il tipo non è constucibile 'sample_rebind' restituisce il tipo originale – xcvr
Passa perché hai usato' using type = T; 'nel caso non specializzato (che viene poi preso). Hai appena combinato tre errori per farlo passare! Rimuovere qualcuno di loro e fallirà. –