2015-06-06 9 views
8

W języku java, aby utworzyć funkcję, która zwraca obiekt, który jest tego samego typu co parametr i rozszerza określoną klasę, chciałbym wpisać:C++ równoważne użycie <T extends Class> dla parametru/zwracanego języka java

public <T extends MyClass> T foo(T bar) {...} 

Czy istnieje odpowiednik C++ tego?

Innymi słowy, w jaki sposób utworzyć funkcję, która przyjmuje dowolną klasę, która rozszerza daną klasę, i zwraca ten sam typ? (Ma to na celu abstrakcyjne/czyste wirtualne klasy).

+0

Taka jest natura szablonów. – AndyG

+0

Java i C++ generic wydają się być zupełnie inne. –

+0

@AndyG Czym dokładnie jest szablon i jak mogę go użyć w tym scenariuszu? – ricky3350

Odpowiedz

8

Możemy użyć enable_if tutaj, jeśli masz C++ 11 lub wyższy dla Ciebie dostępne

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr> 
T Foo(T bar) 
{ 
    return T(); 
} 

Na przykład:

class MyClass 
{ 
public: 
    int a = 1; 
}; 

class Derived : public MyClass 
{ 
public: 
    int b = 2; 
}; 

class NotDerived 
{ 
public: 
    int b = 3; 
}; 

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr> 
T Foo(T bar) 
{ 
    return T(); 
} 

int main() 
{ 
    Derived d; 
    NotDerived nd; 
    std::cout << Foo(d).b << std::endl;; // works 
    //std::cout << (Foo(nd)).b << std::endl;; //compiler error 

    return 0; 
} 

Live Demo

+0

#include musi zostać dodany dla std :: enable_if i std :: is_base_of. – user1056903

+0

@ user1056903: '#include ' ', które mam w demo jest wystarczające i prawdopodobnie preferowane. – AndyG

0

Ponieważ nie mogę wypowiedzieć się na temat zaakceptowana odpowiedź, zapewniam nową odpowiedź, która na nim opiera.

Parametry szablonu można uprościć, zmieniając stan na enable_if na default type template parameter zamiast na nullptr.

8

Technicznie, jak pokazują inne odpowiedzi, istnieją sposoby ograniczenia go do podtypów określonego typu w czasie kompilacji. Jednak w większości przypadków wystarczy po prostu wykonać

template <typename T> T foo(T bar) {...} 

bez konieczności określania powiązania.

W języku Java do generics potrzebne są ograniczenia, ponieważ klasa lub metoda generyczna jest kompilowana niezależnie od jakichkolwiek jej zastosowań. Ogólne klasy lub metody są kompilowane raz, w jedną wersję kodu bajtowego, jedną wersję, która jest w stanie obsłużyć dowolne argumenty, które wywołują wywołujący, które spełniają ograniczenia w deklaracji.

Kompilator musi używać funkcji check-type typu T w treści metody, takich jak wywołania metod, dostępu do pól itp., Nie wiedząc, co to jest T, więc należy podać granicę, aby kompilator mógł być spełniony na przykład wywołanie metody jest poprawne, ponieważ jest zdefiniowane dla wszystkich typów, które spełniają to ograniczenie. Na przykład, jeśli w treści metody użyto wyrażenia bar.baz(), kompilator zezwoli na kompilację tylko wtedy, gdy typ MyClass (a więc wszystkie jego podtypy) zapewnia metodę .baz(); jeśli nie podano żadnych ograniczeń, kompilator narzekałby, że Object (niejawna górna granica) nie ma metody .baz().

Szablony C++ są różne. Klasa lub funkcja szablonu jest "instancjonowana" (ponownie kompilowana) dla każdego innego argumentu, którego jest użyty. Tak więc w momencie kompilowania bryły funkcji dla konkretnej T kompilator wie, czym jest T i jest w stanie bezpośrednio sprawdzać zastosowania tego typu.

Więc jeśli miałeś wyrażenie bar.baz() w ciele funkcji, byłoby dobrze. Jeśli użyłeś tej funkcji z T, która jest typem, który rozszerza MyClass, to skompiluje dobrze, ponieważ taki typ ma .baz().Jeśli użyjesz tej funkcji z typem, który nie ma .baz(), to nie uda się skompilować przy takim użyciu. Jeśli przypadkowo użyjesz funkcji z typem, który nie rozszerza się na MyClass, ale ma .baz(), której typy parametrów i typ powrotu pasują do sposobu, w jaki go używasz, również się skompiluje; ale to niekoniecznie jest złe. Szablony C++ zwykle nie są używane z hierarchiami typów, ale raczej z wymaganiami dotyczącymi tego, jaki typ musi dostarczyć. Na przykład algorytm sortowania nie wymaga, aby jego kontener i/lub typ elementu rozszerzał określony typ, ale raczej, aby kontener zapewniał pewne cechy (np. Operator indeksu dostępu losowego), a typ elementu zapewnia określone funkcje (np. mniejszy niż operator).

Powiązane problemy