2016-03-19 14 views
5

Proszę wziąć pod uwagę następujące tree klasaJak mogę użyć `std :: array` dla parametru szablonu formularza` template <typename> class`?

template<typename T, template<typename> class Tuple> 
class tree 
{ 
private: 
    T m_value; 
    Tuple<tree> m_children; 
}; 

template<typename T, std::size_t N> 
using static_tree = tree<T, std::array<T, N>>; 

która nie jest dobrze zdefiniowana. std::array<T, N> nie jest odpowiednim parametrem szablonu dla Tuple. Zakładam, że zamiar static_tree jest jasny. Moglibyśmy zrobić coś takiego, jak

Czy istnieje inna opcja bez klasy helper?

+0

Jeśli jesteś właścicielem 'tree', dlaczego nie zrobić' Tuple' parametrem typu zamiast szablonu parametr? –

+0

@AlanStokes Jak zdefiniowałabym "static_tree" w takim przypadku? To byłaby niekończąca się rekurencja, prawda? – 0xbadf00d

+0

Faktem jest, że 'std :: array' potrzebuje parametru integer. Nie ma sposobu na określenie go * gdzieś *. Jeśli chodzi o to, gdzie jest to możliwe, istnieje wiele alternatyw, a wasza nie jest daleka od oczywistości. – davidhigh

Odpowiedz

1

Myślę, że twoje pytanie zawiera podstawowy problem, który różni się od tego, o co wyraźnie prosiłeś (trochę wyrzuciło mnie w poprzedniej iteracji tej odpowiedzi). Łącząc różne części twojego pytania, zasadniczo próbujesz utworzyć instancję klasy drzewa, która ma członka, który jest również std::array tej samej klasy. Jest to oczywiście niemożliwe. Prawdopodobnie chcesz, aby drzewo zawierało jakieś Tuple z wskaźników (inteligentnych lub nie).

Jednym ze sposobów, aby to zrobić byłoby użyć klasy pomocnika, ale zmieniając klasę

template<typename T, template<typename> class Tuple> 
class tree 
{ 
    // Indirection (I'm omitting the question of whether these should be 
    //  smart pointers. 
    Tuple<tree<T, Tuple> *> m_children; 
}; 

Innym sposobem stałaby Tuple regularny parametr szablonu, co następuje:

#include <array> 
#include <type_traits> 

template<typename T, class Tuple> 
class tree                                 
{ 
private: 
    static_assert(
     std::is_same<void *, typename Tuple::value_type>::value, 
     "Tuple must be a container of void *"); 

private: 
    T m_value; 
    Tuple m_children; 
}; 

template<typename T, std::size_t N> 
using static_tree = tree<T, std::array<void *, N>>; 

int main() 
{ 
    static_tree<int, 8> t; 
} 

Z jednej strony wyeliminowano klasę pomocników. OTOH, Tuple jest kontenerem z void *: inicjatory zdają sobie z tego sprawę, a klasa wewnętrznie musi wykonywać rzuty. To kompromis. Pozostałbym przy oryginalnej wersji (oczywiście z sugerowanymi modyfikacjami).

+1

Wiesz, że z twoją definicją 'static_tree' to * nie * drzewo, prawda? Dzieci drzewa też muszą być drzewami. – 0xbadf00d

+0

@ 0xbadf00d Masz rację, ale po zastanowieniu, myślę, że było to spowodowane jakimś problemem w twoim pytaniu. Zobacz aktualizację. –

4

Zamiast mieć funkcję pomocnika jako wyjątek dla std::array, proponuję, żeby to była reguła. Zamiast przyjmować parametr szablonu szablonu, weź parametr metafunkcji klasy. Szablon metaprogramowanie jest o wiele łatwiejsze, kiedy wszystko wszędzie jest rodzajem (szablony szablon unikanie i argumenty non-type):

template<typename T, typename TupleMfn> 
class tree 
{ 
private: 
    using Tuple = TupleMfn::template apply<tree>; 

    T m_value; 
    Tuple m_children; 
}; 

z:

template <size_t N> 
struct array_builder { 
    template <class T> 
    using apply = std::array<T, N>; 
}; 

template <typename T, size_t N> 
using static_tree = tree<T, array_builder<N>>; 

To będzie łatwiej korzystać z innych rodzajów pojemniki, a także, ponieważ możemy dokonać otoki dla Templatki, które daje nam z powrotem metafunkcji:

template <template <typename...> class X> 
struct as_metafunction { 
    template <class... Args> 
    using apply = X<Args...>; 
}; 

template <typename T> 
using vector_tree = tree<T, as_metafunction<std::vector>>; 

Jeśli czujesz się szczególnie zadziorna, można zapewnić METAPROGRAMOWANIE w obsłudze wersję std::array:

template <class T, class N> 
struct meta_array : std::array<T, N::value> // obviously I am terrible at naming things 
{ }; 

template <size_t N> 
using size_t_ = std::integral_constant<size_t, N>; 

a następnie podać argumenty zastępcze dla tree zastosowanie:

template <class T, size_t N> 
using static_tree = tree<T, meta_array<_, size_t_<N>>>; 

template <class T> 
using vector_tree = tree<T, std::vector<_>>; 
+0

Twoje 'drzewo' to * nie * drzewo, ponieważ' m_children' jest krotką 'T''. – 0xbadf00d

+0

@ 0xbadf00d Typo – Barry

0

Klasa X nie może zawierać wiele kopii rzeczywistych wystąpień klasy X, z wyjątkiem logicznego.

if mamy

struct X { 
    std::array<X, 2> data; 
}; 

jedyny możliwy rozmiar X nieskończoność, a sizeof(X) = 2*sizeof(X) i wszystkie typy C++ mają sizeof(X)>=1.

C++ nie obsługuje nieskończenie dużych typów.

Twój drugi problem polega na tym, że instancje typu nie są szablonami.

template<typename T, template<typename> class Tuple> 
class tree 

trwa to typ T i templateTuple. Drugi argument nie jest typem.

template<typename T, std::size_t N> 
using static_tree = tree<T, std::array<T, N>>; 

tutaj, twój drugi argument jest rodzajem, nie jest szablon.

template<std::size_t N> 
struct array_of_size { 
    template<class T> 
    using result=std::array<T,N>; 
}; 
template<typename T, std::size_t N> 
using static_tree = tree<T, array_of_size<N>::template result>; 

, inny niż powyższy "problem z nieskończonym rozmiarem", rozwiązać problem. Tutaj mijamy szablon array_of_size<N>::result do tree.

Aby rozwiązać problem z nieskończonym rozmiarem, musisz musi przechowywać wskaźniki (lub coś podobnego) w tablicy. Tak więc otrzymujemy:

template<std::size_t N> 
struct array_of_ups_of_size { 
    template<class T> 
    using result=std::array<std::unique_ptr<T>,N>; 
}; 
template<typename T, std::size_t N> 
using static_tree = tree<T, array_of_ups_of_size<N>::template result>; 

a teraz swoją static_tree ma N dzieci, z których każda stanowi unique_ptr do podobnego static_tree.

To nadal nie działa, z powodu problemów z destruktorem.

template<typename T, template<typename> class Tuple> 
class tree 
{ 
private: 
    T m_value; 
    Tuple<tree> m_children; 
public: 
    ~tree(); 
}; 

template<typename T, template<typename> class Tuple> 
tree<T,Tuple>::~tree() = default; 

Myślę, że powyższe rozwiązuje, dziwne, jak może się wydawać.

Zasadniczo, po utworzeniu tablicy dzieci, typ drzewa jest niekompletny. Podczas destrukcji wywoływane jest usuwanie. W tym momencie drzewo musi być kompletne. Odkładając dtor, mamy nadzieję, że poradzimy sobie z problemem.

Nie jestem pewien, czy ta technika jest wymagana w przypadku szablonów, ale dotyczy klas innych niż szablony.

0

Albo można wdrożyć wiążą parametr szablonu, jak sugeruje (nieco bardziej ogólny niż helper):

template<std::size_t N, template<typename,std::size_t> class T> 
struct bind2nd 
{ 
    template<typename F> 
    using type = T<F,N>; 
}; 

template<std::size_t N, typename T> 
using static_tree = tree<T, bind2nd<N,std::array>::template type>; 
Powiązane problemy