2017-03-29 6 views
2

Bereitstellung I universal "Wrapper" für stl Container, so etwas wie erstellen möchten:Wrapping Behälter für Behälter diffrent Schnittstellen

template<template <typename, typename...> class Container = std::vector > 
class ContainerWrapper{ 
    add(); 
    size(); 
    find(); 
    resize(); 
    sort(); 
    /**/ 
} 

+ Iteratoren. Ich möchte, dass Elementfunktionen unterschiedliche Implementierungen haben, abhängig von den von Container bereitgestellten Methoden. Ist C++ Template System genug um das zu erstellen? Ist das überhaupt möglich, nur mit Standard (kein Boost, kein Durcheinander mit Preprozessor)?

Ich weiß, wie man es auf die harte Tour macht - Template-Spezialisierung für jeden STL-Container schreiben. Aber ich möchte, dass es auch mit anderen Containern funktioniert, und ich suche auch nach einem universelleren Weg, es zu tun.

Auch was ist hier besser? Erben von Container oder Container als Komponente?

+2

Bezüglich der Standardcontainer, werden sie vererbt werden nicht ausgelegt ist. –

+0

"Vererbung wird oft sogar von erfahrenen Entwicklern überstrapaziert." –

+0

Da jeder 'ContainerWrapper' ein nicht verwandter Typ ist, warum willst du das? Ich meine, nicht jeder Std-Container kann "resized" werden. Was passiert, wenn ein "std :: set" übergeben wird, und wie macht das Ihre Lösung besser als nur "std :: set"? Sind Sie süchtig nach Dingen, die Mitglieder eines Typs sind? Warum nicht nur freie Funktionen? Wie 'sort (container)'. – Yakk

Antwort

1

Vor einiger Zeit habe ich etwas ähnliches für ein Projekt von mir entwickelt.

Ich habe ein voll funktionierendes Beispiel (der ursprüngliche Code ist komplexer) extrahiert zu zeigen, wie ein Verfahren verwenden addVal() (diesen Anruf push_back(), push(), insert() oder push_front()) einen Wert in den Behälter eingewickelt hinzuzufügen.

Dieser Code funktioniert (wenn ich mich richtig erinnere) für std::vector, std::set, std::multiset, std::unordered_set, std::unordered_multiset, std::deque, std::queue, std::priority_queue, std::forward_list und std::stack.

Andere Behälter (z. B.-10) erfordern möglicherweise andere Spezialisierungen für cntWrp.

Ich möchte nicht jede einzelne Codezeile erklären, aber wenn Sie Fragen haben, kann ich versuchen zu antworten (oder, wenn Sie es vorziehen, kann ich Ihnen den Link von meinem Github-Projekt geben).

Das Beispiel

#include <set> 
#include <vector> 
#include <iostream> 
#include <stdexcept> 
#include <type_traits> 

class emptyClass 
{ }; 

template <typename ... Ts> 
struct funcType; 

template <typename T, typename ... Ts> 
struct funcType<T, Ts...> 
{ using type 
     = typename std::conditional<T::result, 
     typename T::type, typename funcType<Ts...>::type>::type; }; 

template <> 
struct funcType<> 
{ using type = emptyClass; }; 


#define methodCheck_1(meth)       \ 
                 \ 
    class helpMeth_1_##meth {};       \ 
                 \ 
    template <typename T, typename A>     \ 
    struct isWithMethod_1_##meth      \ 
    {             \ 
     template<typename U>        \ 
     static decltype(U().meth(A())) func (U*);  \ 
                 \ 
     template<typename U>        \ 
     static emptyClass func (...);     \ 
                 \ 
     static const bool result       \ 
     = ! std::is_same<emptyClass,     \ 
       decltype(func<T>(nullptr))>::value; \ 
                 \ 
     using type = helpMeth_1_##meth;     \ 
    } 

methodCheck_1(insert); 
methodCheck_1(push); 
methodCheck_1(push_back); 
methodCheck_1(push_front); 

template <typename> 
class cntWrp; 

template <template <typename ...> class C, typename X, typename ... Xs> 
class cntWrp< C<X, Xs...> > 
{ 
    private: 

     using addModeType = typename funcType< 
     isWithMethod_1_push_back<C<X, Xs...>, X>, 
     isWithMethod_1_insert<C<X, Xs...>, X>, 
     isWithMethod_1_push<C<X, Xs...>, X>, 
     isWithMethod_1_push_front<C<X, Xs...>, X>>::type; 

     static constexpr addModeType addMode {}; 

     void addVal (X const & x, helpMeth_1_push_back const) 
     { val.push_back(x); } 

     void addVal (X const & x, helpMeth_1_push const) 
     { val.push(x); } 

     void addVal (X const & x, helpMeth_1_insert const) 
     { val.insert(x); } 

     void addVal (X const & x, helpMeth_1_push_front const) 
     { val.push_front(x); } 

     void addVal (X const & x, emptyClass const) 
     { throw std::runtime_error("cntWr<>::addVal without mode"); } 

    public: 

     C<X, Xs...> val {}; 

     cntWrp() 
     { } 

     cntWrp (C<X, Xs...> const & v0) : val { v0 } 
     { } 

     void addVal (X const & x) 
     { addVal(x, addMode); } 
}; 

int main() 
{ 
    cntWrp<std::set<int>> csi; 

    csi.addVal(2); 
    csi.addVal(7); 
    csi.addVal(5); 

    std::cout << "set:" << std::endl; 

    for (auto const elem : csi.val) 
     std::cout << elem << std::endl; 

    cntWrp<std::vector<int>> cvi; 

    cvi.addVal(2); 
    cvi.addVal(7); 
    cvi.addVal(5); 

    std::cout << "vector:" << std::endl; 

    for (auto const elem : cvi.val) 
     std::cout << elem << std::endl; 
} 
1

STL-Container sind nicht dazu bestimmt, vererbt zu werden. They do not have virtual destructors. Es wurde diskutiert having the containers as final, aber es wurde nicht getan, weil es eine brechende Änderung wäre.

Also mit Zusammensetzung ist Ihre beste Wette.

+3

Dies ist nicht korrekt, Sie vererben Vererbung und Polymorphie. 'protected' ist eine Obermenge von' public' und daher ist die nicht-polymorphe Vererbung ein gültiger Weg, * any * class zu verwenden (es sei denn, es ist mit 'final' gekennzeichnet). –