• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Specjalizacja szablonów funkcji i zwracanie adresu

Object Storage Arubacloud
0 głosów
348 wizyt
pytanie zadane 23 lipca 2016 w C i C++ przez Coroys Początkujący (370 p.)

Witam. Chciałbym użyć specjalizacji szablonów funkcji (zadanie tego wymaga) do pobrania 5 elementów tablicy char(5 wyrazów) - to pierwszy argument, a drugi to liczba elementów tej tablicy. Funkcja ma zwrócić adres najdłuższego wyrazu.

Tablica wygląda tak:

const char* tabc[5] = {"Hej", "Czescelo", "Brawo", "Siemankos", "test"};

Zrobiłem taki szablon funkcji ze specjalizacją:

template <> const char* maxn(const char** arr, int n)
{
    const char* temp = arr[0];
    int i = 0;    while (i < n)
  {
       if (strlen(arr[i]) > strlen(temp))
          {
              temp = arr[i];
          }
     i++;
   }
    return temp;
}

Funkcja zwraca najdłuższy wyraz, ale tylko wartość. Chciałbym zwrócić ADRES tej wartości. Niestety co próbuje pozmieniać typy w szablonie funkcji, to wyskakują błędy.

O to cały kod:


#include <iostream>
#include <cstring>

using namespace std;

template <class T>
T maxn(T arr [], T n);



template <class T1, class T2>
T1 maxn(T1 arr [], T2 n);


template <> const char* maxn(const char** arr, int n);

int main()
{
    int tab[5] = {5, 8, 2, 4 , 6};
    double tabd[4] = {2.05, 3.87, 3.44, 1.99};
    const char* tabc[5] = {"Hej", "Czescelo", "Brawo", "Siemankos", "test"};
    cout << "Najwiekszy element tablicy int: "<< maxn(tab, 5) << endl;
    cout << "Najwiekszy element tablicy double: "<< maxn(tabd, 4) << endl;
    cout << maxn(tabc, 5);
    return 0;
}

template <class T>
T maxn(T arr [], T n)
{
    T temp = arr[0];
    for (int i = 0; i < 5; i++)
    {
       if(arr[i]>temp)
       {
           temp=arr[i];
       }
    }
    return temp;
}

template <class T1, class T2>
T1 maxn(T1 arr [], T2 n)
{
    T1 temp = arr[0];
    for (int i = 0; i < n; i++)
    {
       if(arr[i]>temp)
       {
           temp=arr[i];
       }
    }
    return temp;
}

template <> const char* maxn(const char** arr, int n)
{
    const char* temp = arr[0];
    int i = 0;
    while (i < n)
    {
       if (strlen(arr[i]) > strlen(temp))
          {
              temp = arr[i];
          }
     i++;
    }
    return temp;
}

 

Od razu mówię, że zadanie wymaga typów char i specjalizacji.

1 odpowiedź

+1 głos
odpowiedź 23 lipca 2016 przez MetRiko Nałogowiec (37,110 p.)

Wypisywanie za pomocą instrukcji cout zmiennej typu const *char sprawia, że w konsoli pojawi się cały tekst od tego znaku do znaku końca tekstu ('\0').
Zapis: 
const char* tabc[2] = {"Hej", "Wow"}; tak na prawdę sprawia, że w pamięci zostaje zadeklarowanych 12 charów: 'H', 'e', 'j', '\0', 'W', 'o', 'w', '\0'. A więc jeżeli w instrucji cout zwracasz funkcją typ const char* to wypisuje się wtedy cały tekst od zwróconego chara do znaku '\0'. To co zwraca twoja funkcja to adres pierwszego znaku najdłuższego "stringa". Jeżeli za adress "wartości" uznamy pierwszy znak tego "stringa" to aby wypisać go w postaci liczbowej proponuję zwracać w funkcji czysty adres (void*), zamiast konkretnego typu. Coś w tym stylu:

const void* maxn(const char** arr, int n) //Koniecznie trzeba zwracać typ const.. ponieważ zmienna temp również jest typu const.. inaczej kod się nie spompiluje.
{
    const char* temp = arr[0];
    int i = 0;
    while (i < n)
    {
       if (strlen(arr[i]) > strlen(temp))
          {
              temp = arr[i];
          }
     i++;
    }
    return temp;
}


 

komentarz 23 lipca 2016 przez Coroys Początkujący (370 p.)
Wszystko fajnie, ale napisałem, że w zadaniu trzeba wykorzystać szablony funkcji i specjalizacje. Prosta funkcje potrafie zrobic, ale ze specjalizacja juz mam problem.
komentarz 23 lipca 2016 przez MetRiko Nałogowiec (37,110 p.)
edycja 23 lipca 2016 przez MetRiko

Jeżeli koniecznie chcesz skorzystać z zapisu template<> to najpierw powinieneś dowiedzieć się co ten zapis tak na prawdę symbolizuje. Najłatwiej będzie to wyjaśnić na prostym przykładzie:

//Jakaś prosta funkcja z użyciem szablonów
template<typename T>
const void* GetAddress(T a)
{
    return static_cast<const void*>(a);
}

//Teraz możemy pokusić się o nadpisanie tej funkcji dla konkretnego typu zmiennej
//W tym wypadku ustalamy, że dla typu T=const char* funkcja dodatkowo wypisze coś na ekranie:
template<>
const void* GetAddress <const char*>(const char* a)
{
    cout<<"To jest nadpisana funkcja";
    return static_cast<const void*>(a)
}

//Zdarzają się jednak takie sytuacje, w których kompilator sam może się domyślić jakim typem zmiennej będzie T
//opierając się na wzorze podstawowej funkcji.. w tym wypadku na podstawie pierwszego argumentu.
//W takich sytuacjach możemy zignorować podawanie typu dla funkcji w nawiasach ostrych
template<>
const void* GetAddress(const char* a) //Tutaj już nie ma zapisu <const void*> oznaczającego, że zmienna T jest równa const void*.. kompilator sam może się tego domyślić
{
    cout<<"To jest nadpisana funkcja";
    return static_cast<const void*>(a)
}

Podsumowując.. jeżeli chcesz skorzystać z zapisu template<> musisz najpierw stworzyć funkcję podstawową z użyciem template<typename T>.
Czyli u ciebie będzie to wyglądać tak:

template <class T>
const void* maxn(T arr, int n)  //T może zastąpić tylko jeden typ zmiennej, typ zwracany oraz zmienna n jest innego typu niż T dlatego nie ustawiamy ich jako T, a jako const void* oraz int
{
   //jakiś tam kod
}

template <>
const void* maxn(const char** arr, int n)
{
    const char* temp = arr[0];
    int i = 0;    
    while (i < n)
    {
        if (strlen(arr[i]) > strlen(temp))
        {
          temp = arr[i];
        }
        i++;
    }
    return static_const<const void*>(temp);
}

 

komentarz 23 lipca 2016 przez Coroys Początkujący (370 p.)

Zaczynam już trochę rozumieć. Specjalizacja ma być dopasowana do szablonu.

Powiedzmy mamy:

template <class T>
T maxn(T arr [], int n);

To specjalizacja

template <> const char* maxn<const char*>(const char* ar[], int n);

Ponieważ typ zwracany i pierwszy parametr są w szablonie o typie T. Czyli w specjalizacji muszą też być takiego samego typu ( w tym przypadku const char*)?

To teraz poprawiłem kod mojego programu (użyłem jednego szablonu i próbuje użyć specjalizacji):

​
#include <iostream>
#include <cstring>

using namespace std;

template <class T>
T maxn(T arr [], int n);

template <> const char* maxn<const char*>(const char* arr[], int n);

int main()
{
    int tab[5] = {5, 8, 2, 4 , 6};
    double tabd[4] = {2.05, 3.87, 3.44, 1.99};
    const char* tabc[5] = {"Hej", "Czescelo", "Brawo", "Siemankos", "test"};
    cout << "Najwiekszy element tablicy int: "<< maxn<int>(tab, 5) << endl;
    cout << "Najwiekszy element tablicy double: "<< maxn<double>(tabd, 4) << endl;
    cout << maxn(tabc, 5);
    return 0;
}

template <class T>
T maxn(T arr [], int n)
{
    T temp = arr[0];
    for (int i = 0; i < 5; i++)
    {
       if(arr[i]>temp)
       {
           temp=arr[i];
       }
    }
    return temp;
}


template <> const char* maxn<const char*>(const char* arr[], int n)
{
    const char* temp = arr[0];
   int i = 0;
    while (i < n)
    {
       if (strlen(arr[i]) > strlen(temp))
          {
              temp = arr[i];
          }
     i++;
    }
    return temp;
}

Teraz tak:

szablon funkcji zwraca największą wartość z tablicy int i double = TO DZIAŁA

specjalizacja tego szablonu, powinna przyjmować tablice ze słowami i zwracać największego słowa = NIE DZIAŁA, OBECNIE UMIEM ZROBIĆ TYLKO ŻEBY ZWRACAŁO NAJWIĘKSZA WARTOŚĆ A NIE ADRES

Z kolei teraz nie mogą zmienić w specjalizacji typu zwracanego z const char * na np. int? W czym przechować adres? Może jakaś konwersja? Koniecznie musi być specjalizacja.

komentarz 23 lipca 2016 przez MetRiko Nałogowiec (37,110 p.)

Po kolei:
Zauważ, że aby nadpisać funkcję:
template <class T> T maxn(T arr [], int n);
musisz mieć taki sam typ zwracany jaki ma pierwszy argument (T jest zwracane, ale znajduje się też w pierwszym argumencie). Aby instrukcja cout zwróciła ci adres zmiennej typu const char* twoja funkcja powinna zwracać sam wskaźnik (bez określonego typu) tj. const void* Jednak ty chcesz ją przeładować w taki sposób aby adres był zwracany tylko w przypadku użycia w pierwszym argumencie typu const char*. A w innym przypadku zwracało zwykłą wartość. Masz dwie możliwości:
1. Stworzyć nową funkcję szablonową: 
template <class T> const void* maxn(T arr [], int n); i ją nadpisać (nie polecam tego rozwiązania).
2. Wykorzystać dwie zmienne dynamiczne tj. T oraz R. R będzie symbolizowało wartość zwracającą, natomiast T będzie pierwszym argumentem.
Prosty przykład użycia dwóch takich zmiennych:

#include <iostream>

template<class T, class R=T> //Typ zmiennej dynamicznej R jest typem zwracanym.. jednak by kompilator wiedział z czym ma do czynienia przypiszemy jej typ T
R GetMax(T *Val, int n) //Funkcja posiada dwa typy zmiennych dynamicznych.. jednak przez przypisanie wartości domyślnej zwróci ten sam typ.. Tak na prawdę R jest nam potrzebne tylko przy przeładowaniu,
//ponieważ będziemy zwracać inny typ niż jest w pierwszym argumencie (tj const void*)
{
    return Val[0]; //Tutaj jest zwracana jakaś wartość
}

template<>
const void* GetMax(const char **Val, int n) //Tutaj kompilator sam przypisuje sobie T=const char**, oraz R=const void* Bez dodatkowej zmiennej dynamicznej R to by nie zadziałało. const char**=/=const void*
{
    return static_cast<const void*>(Val[0]); //Proste rzutowanie na sam wskaźnik bez typu (void*)
}

int main()
{
    int IntTab[5]={5,4,3,2,1};
    const char* CharTab[2]={"Hej", "Wow"};
    std::cout<<GetMax(IntTab, 5)<"\n"; //Tutaj zostanie wypisany pierwszy element Tablicy IntTab
    std::cout<<GetMax(CharTab, 2); //Tutaj zostanie wypisany adres pierwszej litery pierwszego elementu tablicy (w tym wypadku adres 'H')
}

 

komentarz 23 lipca 2016 przez Szykem2 Nałogowiec (29,510 p.)
edycja 23 lipca 2016 przez Szykem2

Nie wiem czy ja nie rozumiem istoty tego problemu, czy Ty nie rozumiesz o co chodzi z łańcuchami znaków. Z poprzedniego komentarza wywnioskowałem, że zwraca Ci poprawne słowo i je wyświetla, a Ty chcesz, żeby wyświetliło adres. Jak już pewnie wiesz nazwa tablicy jest jednocześnie adresem jej pierwszego elementu więc skoro chcesz wyświetlić zwracany wskaźnik na const char'a to wywoływany jest przeładowany operator << obiektu cout dla const char* i od wypisuje cały ciąg znaków. Skoro nazwa to adres pierwszego elementu to jest to wskaźnik na początek tego ciągu, czyli to co chcesz uzyskać, więc nie rozumiem w czym jest problem. Jeśli chcesz żeby wypisało sam adres użyj biblioteki cstdio lub zrzutuj na void*, albo napisz dokładnie co chcesz uzyskać.

#include <cstdio>

int main()
{
    const char* test = "test";
    printf("%p : %s, ", test, test); /%p wstawia adres, %s ciąg znakowy
    cout << static_cast<const void*>(test) << " : " << test << endl;
    return 0;
}
Output: 0x400600 : test, 0x400600 : test

 

komentarz 23 lipca 2016 przez MetRiko Nałogowiec (37,110 p.)
Twórca posta chce przeładować funkcję z użyciem szablonów aby zależenie od tego, czy wprowadzi tablicę int'ów, czy tablicę char'ów od razu zwróciło odpowiedni typ zmiennej.. czyli dla tablicy int'ów największą wartość w tej tablicy, natomiast dla tablicy char'ów, adres pierwszej litery najdłuższego ciągu w tej tablicy (bez dodatkowego rzutowania na typ const void* po wykonaniu funkcji).
komentarz 23 lipca 2016 przez Szykem2 Nałogowiec (29,510 p.)
Czyli tak jak zrozumiałem i napisałem o co chodzi. Łańcuch znaków można traktowań jak tablicę tylko specjalnie wypisywaną przez operator cout. Jeżeli wypisuje cały wyraz(ten który jest najdłuższy) to funkcja zwraca adres pierwszego znaku, a problem jest tylko z odpowiednim wyświetleniem.
komentarz 23 lipca 2016 przez MetRiko Nałogowiec (37,110 p.)

"problem jest tylko z odpowiednim wyświetleniem."
W tym wypadku myślę, że słowo "tylko" tu nie pasuje xD
Bo to właśnie jest najtrudniejsza rzecz w tym wszystkim.. trzeba przerobić funkcję z template<class T> na template<class T, class R=T>

komentarz 23 lipca 2016 przez Szykem2 Nałogowiec (29,510 p.)
Jeżeli autor nie chce używać ani rzutowania ani printf'a z biblioteki C, to może to być trochę cięższe do ogarnięcia, ale mimo wszystko w tym przypadku wystarczy dodać w dwóch miejscach class R i zamienić w 3 miejscach T na R, więc trudnością bym tego nie nazwał, a do tego osobiście bym wywalił przy specjalizacji jawne podawanie typów.
komentarz 23 lipca 2016 przez Coroys Początkujący (370 p.)
#include <iostream>
 
template<class T, class R=T> //Typ zmiennej dynamicznej R jest typem zwracanym.. jednak by kompilator wiedział z czym ma do czynienia przypiszemy jej typ T
R GetMax(T *Val, int n) //Funkcja posiada dwa typy zmiennych dynamicznych.. jednak przez przypisanie wartości domyślnej zwróci ten sam typ.. Tak na prawdę R jest nam potrzebne tylko przy przeładowaniu,
//ponieważ będziemy zwracać inny typ niż jest w pierwszym argumencie (tj const void*)
{
    return Val[0]; //Tutaj jest zwracana jakaś wartość
}
 
template<>
const void* GetMax(const char **Val, int n) //Tutaj kompilator sam przypisuje sobie T=const char**, oraz R=const void* Bez dodatkowej zmiennej dynamicznej R to by nie zadziałało. const char**=/=const void*
{
    return static_cast<const void*>(Val[0]); //Proste rzutowanie na sam wskaźnik bez typu (void*)
}
 
int main()
{
    int IntTab[5]={5,4,3,2,1};
    const char* CharTab[2]={"Hej", "Wow"};
    std::cout<<GetMax(IntTab, 5)<"\n"; //Tutaj zostanie wypisany pierwszy element Tablicy IntTab
    std::cout<<GetMax(CharTab, 2); //Tutaj zostanie wypisany adres pierwszej litery pierwszego elementu tablicy (w tym wypadku adres 'H')
}

Ten kod nie zwraca żadnego adresu, tylko wartość pierwszego elementu tablicy. Nawet jak próbuje zmienić element w [] to i tak wyświetla ciągle wartość pierwszego elementu tablicy. Chciałbym sam adres zwracać, żeby:

cout << maxn(tabc, 5);

Pokazywało mi adres danego elementu tablicy.

Podobne pytania

+1 głos
0 odpowiedzi 351 wizyt
0 głosów
1 odpowiedź 317 wizyt
0 głosów
2 odpowiedzi 2,197 wizyt
pytanie zadane 15 października 2016 w C i C++ przez Rumci Nowicjusz (220 p.)

92,632 zapytań

141,499 odpowiedzi

319,878 komentarzy

62,011 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...