2016-08-13 13 views
9

jeśli mam szablon:przechowywanie i ponowne używanie wartości decltype?

template <class T> 
struct Item 
{ 
    T _value; 
}; 

mogę wtedy zrobić:

// ... 
Item<int> x = { 42 }; // declared as an int 

// ... 
decltype(x._value) y = 20; // 'y' is also an int 

Ale czy jest to możliwe do przechowywania decltype do zmiennej, dzięki czemu można go stosować później?

Dlaczego?
Chcę przechowywać wartości elementów jako wskaźnik.

Coś std::vector<Item*> ale jak są szablony muszę przechowywać je jako wskazówki do void:

std::vector<void*> is; 
is.push_back(new Item<int>()); 
is.push_back(new Item<double>()); 
is.push_back(new Item<float>()); 

I to wszystko jest w porządku, ale gdy przychodzi czas, aby usunąć wskaźnik muszę ponownie obsady moja void* z powrotem do właściwego typu (tak nazywane są destruktory):

delete (Item<int>*)is[0]; 

A jeśli wiem, typ, mogę zrobić:

delete (Item<decltype(whatever)>*)is[0]; 

Z tego powodu muszę przechowywać decltype.

Mam nadzieję, że to ma sens.

+2

Powinieneś sprawdzić Boost.variant i Boost.any (lub ich odpowiedniki C++ 17 [ 'std :: variant'] (http: // pl. cppreference.com/w/cpp/utility/variant) i ['std :: any'] (http://en.cppreference.com/w/cpp/utility/any)). Są prawdopodobnie lepszymi implementacjami tego, czego potrzebujesz. –

+0

Typ wymazania jest słowem kluczowym, w C jest wykonywane z 'void *', w C++ jest wykonywane z 'std :: any' oraz wzorem gościa. Rozwiązania inteligentnych wskaźników używają wewnętrznie pewnego rodzaju usuwania typów. – alfC

+0

@alfC Zauważ, że wymazywanie typów odbywa się w czasie kompilacji, podczas gdy podstawową ideą systemu odwiedzającego jest podwójna wysyłka, czyli mechanizm wykonawczy. – skypjack

Odpowiedz

6

decltype to funkcja językowa, która umożliwia pobieranie typu podczas kompilacji. Wygląda na to, że chcesz "przechowywać" ten typ, aby móc poprawnie umieszczać obiekty w pamięci dynamicznej w czasie wykonywania. Zakładając, że tak jest, decltype nie pomoże tutaj.

Masz różne opcje:

  1. korzystać z niektórych form typu kasowania obiektu jak Boost.Variant lub Boost.Any, jak sugeruje Baum mit Augen w komentarzach.

  2. Dokonać obiekty część polimorficznych hierarchii i użyj inteligentne kursory:

    struct ItemBase 
    { 
        virtual ~ItemBase() { } 
    }; 
    
    template <class T> 
    struct Item : ItemBase 
    { 
        T _value; 
    }; 
    
    int main() 
    { 
        std::vector<std::unique_ptr<ItemBase>> items; 
        items.emplace_back(std::make_unique<Item<int>>()); 
        items.emplace_back(std::make_unique<Item<float>>());      
        items.emplace_back(std::make_unique<Item<double>>()); 
    } 
    
+0

Prawnicy języków powiedzą, że nie ma żadnego miejsca, do którego można by się przydzielić. _Dynamiczna pamięć może być bardziej przywłaszczona. – skypjack

+1

@skypjack: dobry punkt, poprawiono odpowiedź. Dzięki! –

+0

Numer 2 jest zdecydowanie najprostszy i najbardziej wydajny. Może istnieć nawet wspólna funkcjonalność, która kończy się w ItemBase! – rubenvb

5

Jeśli problem jest tylko, aby je usunąć, można użyć unique_ptr z niestandardowym Deleter zamiast nagich wskaźników.
Aby to zrobić, nie trzeba modyfikować hierarchii.
Jako przykład:

std::vector<std::unique_ptr<void, void(*)(void*)>> is; 
is.push_back(std::unique_ptr<void, void(*)(void*)>{new Item<int>(), [](void *ptr) { delete static_cast<Item<int>*>(ptr); }}); 

Jeszcze lepiej, jeśli za pomocą emplace_back zamiast push_back:

std::vector<std::unique_ptr<void, void(*)(void*)>> is; 
is.emplace_back(new Item<int>(), [](void *ptr) { delete static_cast<Item<int>*>(ptr); }); 

Wynika minimalny, przykład działa na podstawie kodeksu PO za:

#include<vector> 
#include<memory> 

template<typename> 
struct Item {}; 

int main() { 
    using Deleter = void(*)(void*); 
    std::vector<std::unique_ptr<void, Deleter>> is; 
    is.emplace_back(new Item<int>(), [](void *ptr) { delete static_cast<Item<int>*>(ptr); }); 
    is.emplace_back(new Item<double>(), [](void *ptr) { delete static_cast<Item<double>*>(ptr); }); 
    is.emplace_back(new Item<float>(), [](void *ptr) { delete static_cast<Item<float>*>(ptr); }); 
} 
+0

Wielkie dzięki, fajny i czysty sposób robienia tego. –

1

ty może przechowywać przetwornik

więc z std::shared_ptr, staje się:

std::vector<std::shared_ptr<void>> items; 

// The simplest 
items.push_back(std::make_shared<Item<int>>(/*args...*/)); 

// Explicitly specify the (default) deleter 
items.push_back(std::shared_ptr<void>{new Item<double>(/*args...*/), 
             std::default_delete<Item<double>>{}}); 

// Explicitly specify the (default) allocator 
items.push_back(
    std::allocate_shared<Item<float>>(std::allocator<Item<float>>{} /*, args...*/); 
Powiązane problemy