मैं पुनर्जन्म के एक ठोस ठोस पहले मसौदे को पाने में कामयाब रहा हूं। यह सभी एसटीएल कंटेनर (टेम्पलेट पैरामीटर के कम आम संयोजन को छोड़कर), कंटेनर एडेप्टर, और std::integer_sequence
के लिए काम करता है। और यह शायद बहुत अधिक चीजों के लिए भी काम करता है। लेकिन यह निश्चित रूप से सब कुछ के लिए काम नहीं करेगा।
मुख्य समस्या को मानचित्र की तरह काम करने के लिए यक के भविष्यवाणी के रूप में काम करना पड़ रहा था, लेकिन थोड़ी सी विशेषता के साथ मदद मिली।
तो कोड के लिए पर ...
void_t
template<class...>
using void_t = void;
वाल्टर ई ब्राउन ने इस छोटे चाल बनाता प्रकार को लागू करने के लिए एक बहुत आसान लक्षण।
प्रकार लक्षण
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
तथ्य यह है कि एसटीएल में नक्शे की तरह प्रकार के सभी value_type
एक (एन) होने के लिए परिभाषित किया है का उपयोग करता है std::pair
const
साथ एड की पहली टेम्पलेट पैरामीटर नक्शा की तरह प्रकार, first_type
pair
में। मानचित्र-जैसे प्रकार का दूसरा टेम्पलेट पैरामीटर बिल्कुल pair
के second_type
से मेल खाता है। rebind
को नक्शा-तरह के प्रकारों को अधिक सावधानी से संभालना है।
has_mem_rebind
चाल का उपयोग कर T
पर सदस्य rebind
मेटा-फ़ंक्शन की उपस्थिति का पता लगाता है। यदि किसी वर्ग में rebind
है तो हम पहले कक्षा कार्यान्वयन को रोक देंगे।
is_template_instantiation
पता लगाता है कि T
एक टेम्पलेट तत्काल है या नहीं। यह डिबगिंग के लिए अधिक है।
हेल्पर प्रकार सूची
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...>;
};
इस प्रकार के जोड़तोड़ की तरह कुछ सरल सूची
replace
एक गैर पुनरावर्ती फैशन में U
साथ T
की सभी घटनाओं की जगह संभालती है।
replace_or_rebind
U
साथ T
की सभी घटनाओं की जगह है, और सभी मेल नहीं खाने वाले घटनाओं के लिए, कॉल rebind
replace_or_rebind_if_not
replace_or_rebind
रूप में ही है, लेकिन किसी भी मिलान Not
push_front
बस सामने करने के लिए पर एक तत्व धक्का तत्व पर छोड़ देता है टाइप-सूची
के कॉलिंग सदस्य 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;
};
यह मानता है कि मानक के अनुसार सदस्य rebind
को लागू करने के दो अलग-अलग मान्य तरीके हैं। allocators के लिए यह rebind<T>::other
है। pointers के लिए यह सिर्फ rebind<T>
है। do_mem_rebind
का यह कार्यान्वयन rebind<T>::other
के साथ आता है यदि अन्यथा यह मौजूद है, अन्यथा यह rebind<T>
पर आसान हो जाता है।
अनपैक
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;
यह एक pack
लेता है, प्रकार इसमें निकालता है, और उन्हें कुछ अन्य टेम्पलेट C
में डालता है।
rebind कार्यान्वयन
अच्छी चीजें।
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>>;
};
- मामले असफल
rebind
के लिए बस प्रकार अपरिवर्तित रखने के लिए है। यह rebind<Types, double>...
पर कॉल करने की अनुमति देता है कि Type
Types
rebind
सक्षम है या नहीं। यदि इसमें टेम्पलेट इंस्टेंटेशन प्राप्त होता है तो वहां static_assert
है। यदि यह हिट है, तो आपको शायद rebind
- का एक और विशेषज्ञता चाहिए। नक्शा की तरह
rebind
rebind<std::map<int, int>, std::pair<double, std::string>>
जैसे मानचित्र की अपेक्षा की जाती है। तो जिस प्रकार आवंटक को रिबाउंड किया जा रहा है, उस प्रकार से मेल नहीं खा रहा है जिस पर कंटेनर को रिबाउंड किया जा रहा है। यह if_not
allocator_type
होने के साथ, कुंजी और मान प्रकार को छोड़कर सभी प्रकारों पर replace_or_rebind_if_not
करता है। चूंकि आवंटक प्रकार कुंजी/मान जोड़ी से भिन्न होता है rebind
जोड़ी के पहले तत्व के const
नेस को संशोधित करने की आवश्यकता है। यह आवंटक को पुनर्निर्मित करने के लिए std::allocator_traits
का उपयोग करता है, क्योंकि सभी आवंटकों को std::allocator_traits
के माध्यम से पुन: प्रयोज्य होना चाहिए।
- यदि
T
में सदस्य rebind
है, तो इसका उपयोग करें।
T
कोई भी सदस्य rebind
, replace_or_rebind
से मेल खाने वाले C
के पहले टेम्पलेट पैरामीटर टेम्पलेट C
के सभी मापदंडों है।
- यदि
T
में एक प्रकार का पैरामीटर है, और गैर-प्रकार टेम्पलेट पैरामीटर का एक गुच्छा जिसका प्रकार पैरामीटर से मेल खाता है। उन सभी गैर-प्रकार पैरामीटर को U
पर पुन: प्रयास करने का प्रयास करें। यह वह मामला है जो std::integer_sequence
काम करता है।
std::array
के लिए एक विशेष मामला आवश्यक था क्योंकि इसमें एक गैर-प्रकार का टेम्पलेट पैरामीटर होता है जो इसे आकार देता है, और वह टेम्पलेट पैरामीटर अकेला छोड़ा जाना चाहिए।
- यह मामला अन्य पॉइंटर प्रकारों के पॉइंटर्स को रिबाइंड करने की अनुमति देता है। यह इसे पूरा करने के लिए
std::pointer_traits
के rebind
का उपयोग करता है। T[5]
- के बिना एक आकार पूर्व सी सरणियों पर
rebind
काम करने देता है: T[]
rebind
रों lvalue-रेफरी T
एक गारंटीकृत lvalue-रेफरी std::remove_reference_t<U>
करने के लिए प्रकार
- पर सी सरणियों आकार पूर्व
rebind
काम करने देता है।
rebind
s rvalue-ref T
प्रकार एक गारंटीकृत रावल्यू-std::remove_reference_t<U>
पर रीफ्रेश करें।
व्युत्पन्न (उजागर) वर्ग
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;
वापस SFINAE करने के लिए और static_assert
ज्यादा googling वहाँ की तरह चारों ओर static_assert
रों SFINAE लिए एक क्रियाशील रास्ता होना प्रतीत नहीं होता है के बाद libC++ के एसटीएल कंटेनर में से एक। यह वास्तव में मेरी इच्छा करता है कि भाषा में कुछ और अधिक SFINAE दोस्ताना हों, लेकिन अवधारणाओं की तुलना में थोड़ा अधिक विज्ञापन-प्रसार।
तरह:
template<class T>
static_assert(CACHE_LINE_SIZE == 64, "")
struct my_struct { ... };
आपका प्रश्न उदाहरण आप दे काम कभी नहीं होगा के रूप में स्पष्ट नहीं है। क्या आपका मतलब है 'sample_rebind, int> '? और इसके परिणामस्वरूप 'fake_cont >' यदि आप 'अन्य आर्ट्स' रखते हैं ... '। चूंकि यह वर्तमान में खड़ा है, संकलक आपके कोड को अस्वीकार करने के लिए बस सही है। –
मैंने 'fake_cont' में 'static_assert' को टिप्पणी की, और यह वास्तव में अंतिम static_assert को पार करते हुए संकलित (कम से कम 3.5 पर संकलित) करता है। मैं यह नहीं कह रहा कि संकलक गलत है, मैं सिर्फ यह पूछ रहा हूं कि मैं विशेषज्ञता के 'is_constructible' भाग पर संकलक कैसे विफल नहीं कर सकता। ध्यान दें कि यदि प्रकार का निर्माण नहीं किया गया है तो 'sample_rebind' मूल प्रकार – xcvr
लौटाता है क्योंकि यह गैर-विशिष्ट मामले (जिसे तब लिया जाता है) में' टाइप प्रकार = टी; 'का उपयोग किया जाता है। आपने इसे पास करने के लिए अभी तीन त्रुटियों को जोड़ा है! उनमें से किसी को हटा दें और यह असफल हो जाएगा। –