2013-02-28 34 views
5

Mam prosty szablon struct przypisujące ciąg o wartościSzablony przyjmowanie „coś” w C++

template<typename T> struct Field 
{ 
    std::string name; T self; 
} 

Mam funkcji, które chcę, aby zaakceptować 1-lub-więcej pól dowolnego typu, a Pola mogą być różnych typów, więc używam std::initializer_list, ponieważ C++, według mojej wiedzy, nie ma wypisanych argumentów variadic, nie może określić rozmiaru argumentów variadic i musi mieć co najmniej jeden inny argument, aby określić, od czego zacząć.

Problem polega na tym, że nie wiem, jak powiedzieć, aby zaakceptować pola, które mogą być różnych typów. W Javie użyłbym po prostu foo(Field<?> bar, Field<?>... baz), ale C++ nie ma obu wpisanych argumentów variadycznych i symboli wieloznacznych. Moim jedynym pomysłem jest ustawienie parametru typu std::initializer_list<Field<void*>>, ale wydaje się, że to złe rozwiązanie ... Czy jest lepszy sposób na zrobienie tego?

+2

Inicjacje tego samego szablonu z różnymi argumentami szablonu są całkowicie niezwiązanymi typami. Co masz zamiar zrobić z obiektami 'Field', gdy je już masz? C++ * ma * szablony variadyczne (http://en.wikipedia.org/wiki/Variadic_template) (które mogą być używane do tworzenia funkcji variadycznych typu safe). – Mankarse

+0

funkcja będzie tylko przechowywała każde z pól w wektorze na później, więc naprawdę nie obchodzi mnie, jakie są ich typy _are_. Bezpieczeństwo typowe dla variadics w C++ jest naprawdę poważnym problemem, ponieważ jeśli kod ulegnie awarii podczas wysyłania błędnych typów, dobrze nie rób tego wtedy (czyli defensywnie i sprawdź typy osobiście przed wywołaniem funkcji) . Wiem, że Field i Field nie są w rzeczywistości powiązane (nie-(współwystępowanie) -variancji uniemożliwiło (no, nie do końca, ale było niewygodne) parametrów szablonów innych niż klasowe). –

+0

@MattG Więc wszystkie pola mają postać _one_ vector? W takim przypadku jeszcze trudniejszym pytaniem będzie, jaki powinien być typ tego wektora. – jogojapan

Odpowiedz

0

To nie jest bardzo idiomatyczne dla C++. Można to zrobić; Książka Coplien może mieć kilka pomysłów. Ale C++ jest silnie typowany, ponieważ wierzy w pisanie na klawiaturze; próba przekształcenia go w Smalltalk lub złożenie go jak bażanta może doprowadzić do łez.

4

Generic Java jest bliższy wypełnianiu boost::any w zmiennej self niż w C++. Spróbuj. Szablony C++ tworzą typy, które domyślnie nie mają nawiązywania ze środowiskiem wykonawczym ani relacjami dynamicznymi.

Możesz wprowadzić taką relację ręcznie, na przykład za pomocą wspólnego rodzica i typu wymazania i rozsądnego użycia pImpl i inteligentnych wskaźników.

Typowe argumenty typu C są nieobrabiane w C++ 11. Argumenty w szablonie Variardic są bardzo bezpieczne, tak długo jak twój kompilator obsługuje je (CTP 2012 dla MSVC 2012 ma wsparcie dla nich (nie aktualizacja 1, CTP), podobnie jak clang i nieaktualne wersje gcc).

Szablony w C++ to rodzaj metaprogramowania, bliższego napisania programu, który pisze program niż Java Generics. Java Generic ma jedną wspólną implementację "binarną", podczas gdy każda instancja szablonu C++ jest zupełnie innym "programem" (który, za pomocą takich procedur jak składanie COMDAT, można zredukować do jednej implementacji binarnej), którego szczegóły są opisane przez szablon kod.

template<typename T> 
struct Field { 
    T data; 
}; 

to mały program, który mówi "tutaj jest jak tworzyć typy pól". Po przejechaniu w int i double, kompilator robi coś mniej więcej takiego:

struct Field__int__ { 
    int data; 
}; 
struct Field__double__ { 
    double data; 
}; 

i nie można spodziewać się te dwa rodzaje być wymienialne pomiędzy.

rodzajowych Java, z drugiej strony, stworzyć coś takiego:

struct Field { 
    boost::any __data__; 
    template<typename T> 
    T __get_data() { 
    __data__.get<T>(); 
    } 
    template<typename T> 
    void __set_data(T& t) { 
    __data__.set(t); 
    } 
    property data; // reading uses __get_data(), writing uses __set_data() 
}; 

boost::any gdzie jest kontener, który może pomieścić wystąpienie dowolnego typu, a dostęp do pola data przekierowuje przez te dostępowych.

C++ zapewnia możliwość napisania czegoś podobnego do generycznych Java przy użyciu metaprogramowania szablonów. Aby napisać coś takiego jak szablony C++ w Javie, musielibyśmy mieć własny wyjściowy bajt Javy w języku Java lub kod źródłowy, a następnie uruchomić ten kod w taki sposób, aby debugger mógł się połączyć z kodem, który zapisuje kod jako źródło błędów.

7

Kilka rzeczy ...

  • C++ 11 (które wydaje się, że skoro mówisz std::initializer_list) ma wpisane zmiennej liczbie argumentów argumentów, w szczególności są one nazwane o zmiennej liczbie argumentów szablony

  • Generics Java i C++ to zupełnie inne bestie. Generics Java tworzy pojedynczy typ, który przechowuje odniesienie do Object i zapewnia automatyczne rzutowanie i wysyłanie do typów w interfejsie, ale ważnym jest to, że wykonuje on wymazywanie typu.

Polecam, aby wyjaśnić problem, który chcesz rozwiązać, i uzyskać sugestie dotyczące rozwiązania problemu, które są idiomatyczne w C++. Jeśli chcesz naprawdę naśladować zachowanie w Javie (które, nie mogę wystarczająco nalegać, to inny język i ma inne idiomy), możesz ręcznie wymazać typ w C++ ręcznie (tj. Użyć boost::any). Ale bardzo rzadko czuję potrzebę całkowitego wymazania typów w programie ... używanie wariantu wariantowego (boost::variant) jest nieco bardziej powszechne.

Jeśli kompilator posiada wsparcie dla zmiennej liczbie argumentów szablonów (nie wszystkie kompilatory zrobić), zawsze można grać z tym, ale stashing pola na później w wektorze może być nieco skomplikowane dla w pełni ogólne podejście, chyba że używasz typ wymazania. (Ponownie, jaki jest problem do rozwiązania? Mogą istnieć prostsze rozwiązania ...)

+1

+1 dla "generics i szablony są różne" inny +1 dla "Java i C++ mają różne idiomy" i kolejne +1 dla "co to jest podstawowy problem do rozwiązania" –

+0

@MarkB 2 kont sockpuppet? : p ~ – Yakk

1

Nie ma potrzeby używania symboli wieloznacznych w szablonach C++, ponieważ w C++ zawsze rozpoznaje typ i nie jest "kasowany" w Javie. Aby napisać void foo(Field<?> bar, Field<?>... baz) metody (lub funkcja) w C++, należy napisać:

template<class T, class... Ts> 
void foo(Field<T> bar, Field<Ts>... baz); 

Każdy Field<Ts> może być inny typ. Aby użyć parametrów variadic w funkcji, wystarczy użyć baz.... Więc mówisz, że chcesz wywołać inną funkcję:

template<class T, class... Ts> 
void foo(Field<T> bar, Field<Ts>... baz) 
{ 
    foo2(baz...); 
} 

Możesz również rozszerzyć typ z Field<Ts>..., więc jeśli chcesz umieścić go na krotki (nie można umieścić je w tablicy, ponieważ mogą one być różne rodzaje):

template<class T, class... Ts> 
void foo(Field<T> bar, Field<Ts>... baz) 
{ 
    std::tuple<Field<Ts>...> data(baz...); 
} 
+0

Co ze 'std :: tuple '? Czy to jest możliwe? – user833771