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

question-closed Nieszablonowe funkcje zaprzyjaźnione szablonu - c++ Szkoła Programowania

Cloud VPS
+1 głos
963 wizyt
pytanie zadane 5 października 2018 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 7 października 2018 przez Jakub 0

Witam, to jest kod przepisany ze wspomnianej książki z listingu 14.22 :

szablon klasy:

#ifndef FRND2TMP_H_
#define FRND2TMP_H_

#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class HasFriend {
private:
	T item;
	static int ct;
public:
	HasFriend(const T& i) : item(i) { ct++; }
	~HasFriend() { ct--; }
	friend void counts();
	friend void reports(HasFriend<T>&);
};

//statyczna składowa dla każdej specjalizacji
template <typename T> int HasFriend<T>::ct = 0;

//funkcja zaprzyjaźniona ze wszystkimi klasami HasFriend<T>
void counts() {
	cout << "Konkretyzacje int: " << HasFriend<int>::ct << "; ";
	cout << "Konkretyzacje double: " << HasFriend<double>::ct << endl;
}

//funkcje zaprzyjaźnione dla konkretnych konkretyzacji

void reports(HasFriend<int>& hf) {
	cout << "HasFriend<int>: " << hf.item << endl;
}

void reports(HasFriend<double>& hf) {
	cout << "HasFriend<double>: " << hf.item << endl;
}

#endif

Użycie w innym pliku:

#include <iostream>
#include "frnd2tmp.h"

void main10() {
	using std::cout;
	using std::endl;
	cout << "Brak zadeklarowanych obiektow: ";
	counts();
	HasFriend<int> hfi1(10);
	cout << "Po deklaracji hfi1: ";
	counts();
	HasFriend<int> hfi2(20);
	cout << "Po deklaracji hfi2: ";
	counts();
	HasFriend<double> hfdb(10.5);
	cout << "Po deklaracji hfdb: ";
	counts();

	reports(hfi1); // 1
	reports(hfi2); // 2
	reports(hfdb); // 3
}

Ostateczne wywołanie void main10() jest oczywiście w funkcji int main().

Ostatnie wywołania funkcji zaprzyjaźnionej reports() zaznaczyłem jako 1, 2, 3. Bo tu kompilator daje mi taki błąd: "więcej niż jedno wystąpienie elementu funkcja przeciążona "reports" jest zgodne z listą argumentów"

Nie rozumiem dlaczego :/ Przecież mam w definicjach ładnie napisane specjalizacje tej funkcji:

void reports(HasFriend<int>& hf) {
	cout << "HasFriend<int>: " << hf.item << endl;
}

void reports(HasFriend<double>& hf) {
	cout << "HasFriend<double>: " << hf.item << endl;
}

W książce pisze że niektóre kompilatory ostrzegają o próbie stosowania nieszablonowych funkcji zaprzyjaźnionych, ja za to mam błąd...

Korzystam z visual studio 2017, ja coś źle zrobiłem czy po prostu wiele kompilatorów nie toleruje już takich konstrukcji w ogóle ? ( o ile w książce piszę tylko o ostrzeganiu ). Z góry wam dziękuje za pomoc.

komentarz zamknięcia: temat wyczerpany
1
komentarz 5 października 2018 przez RafalS VIP (122,820 p.)
Przestaw język błędów na agielski bo odbierasz sobie drugi po restarcie najbardziej informatyczny sposób naprawy wszystkiego - googlowanie błędu :P

3 odpowiedzi

+1 głos
odpowiedź 5 października 2018 przez j23 Mędrzec (195,240 p.)
wybrane 6 października 2018 przez Jakub 0
 
Najlepsza

Daj tak:

template<typename U> friend void reports(HasFriend<U>&);

/* ... */

//funkcje zaprzyjaźnione dla konkretnych konkretyzacji
template<typename U> 
void reports(HasFriend<U>&);

template<> 
void reports<int>(HasFriend<int>& hf) {
    cout << "HasFriend<int>: " << hf.item << endl;
}
 
template<> 
void reports<double>(HasFriend<double>& hf) {
    cout << "HasFriend<double>: " << hf.item << endl;
}

powinno przejść.

komentarz 6 października 2018 przez Jakub 0 Pasjonat (23,120 p.)

Dzięki, taki sposób moim zdaniem jest najlepszy. Ogólnie jednak kod który jest podany w książce i który ja wrzuciłem do pytania  ma pokazywać jak to zrobić używając nieszablonowych funkcji zaprzyjaźnionych. Tu natomiast są użyte szablony. Dalej w rozdziale też mam podany ten sposób i wygląda to tak:

template<typename T> void counts();
template<typename T> void report(T&);

template <typename TT>
class HasFriendT {
private:
	TT item;
	static int ct;
public:
	HasFriendT(const TT& i) : item(i) { ct++; }
	~HasFriendT() { ct--; }
	friend void counts<TT>();
	friend void report<>(HasFriendT<TT>&);
};

template <typename T> int HasFriendT<T>::ct = 0;

template<typename T> void counts() {
	cout << "Szablon sizeof: " << sizeof(HasFriendT<T>) << "; ";
	cout << "Szablon counts(): " << HasFriendT<T>::ct << endl;
}

template <typename T> void report(T& hf) {
	cout << hf.item << endl;
}

Mam tu mały problem ze zrozumieniem tego w definicji szablonu klasy:

friend void counts<TT>();
friend void report<>(HasFriendT<TT>&);

Piszę tu że dzięki temu szablony o danej specjalizacji, np:

friend void counts<int>();
friend void report<int>(HasFriendT<int>&);

będą zaprzyjaźnione z klasą: HasFriendT<int> itd...

Nie rozumiem tu tylko określenia tych specjalizacji, z tego co wiem piszę się to tak:

template <> typ nazwa_funkcji<typ>(argumenty o danym typie);

w ten sposób mamy prototyp specjalizacji szablonu funkcji, dlaczego w podanym przykładzie z książki nie ma przed friend void counts<TT>() i friend void report<>(HasFriendT<TT>&) jeszcze oznaczenia template<> ?

Z góry dziękuje za pomoc :)

1
komentarz 6 października 2018 przez j23 Mędrzec (195,240 p.)

Nie ma, bo template<> dajesz tylko przy definiowaniu/deklarowaniu specjalizacji. Tutaj nie zaprzyjaźniasz specjalizacji, tylko funkcje zwykłe lub szablonowe.

BTW, ten twój pierwszy kod powinien się skompilować (g++ daje tylko ostrzeżenie, czy aby na pewno chodziło mi o zaprzyjaźnienie nieszablonowej funkcji)

komentarz 6 października 2018 przez Jakub 0 Pasjonat (23,120 p.)

Nieco bardziej to rozumiem, chciałbym się tylko zapytać jak profesjonalnie się to nazywa ( taki zabieg ):

friend void counts<TT>();
friend void report<>(HasFriendT<TT>&);

Ja to rozumiem tak że konkretyzacja szablonu klasy przyjaźni się z szablonami funkcji skonkretyzowanymi tym samym typem.

Przepraszam za taką moją dociekliwość czy też kompletny brak zrozumienia, ale czasami tak bywa...

1
komentarz 6 października 2018 przez j23 Mędrzec (195,240 p.)

Ten zabieg to po prostu zaprzyjaźnianie funkcji. W przypadku report dodając <> dajesz do zrozumienia, że chodzi o zaprzyjaźnienie szablonu funkcji.

template <typename T> class HasFriend;
template <typename T> void reports(HasFriend<T>&);

template <typename T>
class HasFriend {

    /* ... */

    friend void reports<>(HasFriend<T>&);
};

Takie zaprzyjaźnienie nie zadziała z funkcjami reports z pierwszego postu, ponieważ nie obie funkcje nie są szablonami. Zrób je specjalizacjami szablonu z drugiej linii, a wszystko się skompiluje.

+1 głos
odpowiedź 5 października 2018 przez monika90 Pasjonat (22,940 p.)
edycja 5 października 2018 przez monika90

Moim zdaniem kod jest poprawny.

Możesz przed przed definicją szablonu klasy dodać takie deklaracje

template<typename T> class HasFriend;
void reports(HasFriend<int>&);
void reports(HasFriend<double>&);

być może to pomoże kompilatorowi się ogarnąć. Nie mam tego kompilatora, więc nie mogę tego sprawdzić.

 

Przecież mam w definicjach ładnie napisane specjalizacje tej funkcji:

Ale to są przeciążenia, a nie specjalizacje.

A w ogóle, komunikaty kompilatora podawaj w języku angielskim - łatwiej je będzie zrozumieć.

komentarz 6 października 2018 przez Jakub 0 Pasjonat (23,120 p.)

Ale to są przeciążenia, a nie specjalizacje.

Racja...

+1 głos
odpowiedź 5 października 2018 przez mokrowski Mędrzec (158,900 p.)
edycja 5 października 2018 przez mokrowski
#ifndef FRND2TMP_H_
#define FRND2TMP_H_

#include <iostream>

// NIGDY W NAGŁÓWKU!!!
//using std::cout;
//using std::endl;

template <typename T>
class HasFriend {
private:
    static int ct;
    T item;
public:
    HasFriend(const T& i) : item(i) { ct++; }
    ~HasFriend() { ct--; }
    friend void counts();
    friend void reports(HasFriend<T>&);
};

template <typename T> int HasFriend<T>::ct = 0;

// Jeśli chcesz by counts() wiedział o funkcjach reports..., to pwinny być wimienione przed
//funkcje zaprzyjaźnione dla konkretnych konkretyzacji
void reports(HasFriend<int>& hf) {
    std::cout << "HasFriend<int>: " << hf.item << std::endl;
}

void reports(HasFriend<double>& hf) {
    std::cout << "HasFriend<double>: " << hf.item << std::endl;
}

void counts() {
    std::cout << "Konkretyzacje int: " << HasFriend<int>::ct << "; ";
    std::cout << "Konkretyzacje double: " << HasFriend<double>::ct << std::endl;
}

#endif
#include <iostream>
#include "frnd2tmp.h"
 
int main() { // void main10() ? A jak program ma wejść do tej funkcji?
    using std::cout;
    using std::endl;
    cout << "Brak zadeklarowanych obiektow: ";
    counts();
    HasFriend<int> hfi1(10);
    cout << "Po deklaracji hfi1: ";
    counts();
    HasFriend<int> hfi2(20);
    cout << "Po deklaracji hfi2: ";
    counts();
    HasFriend<double> hfdb(10.5);
    cout << "Po deklaracji hfdb: ";
    counts();
 
    reports(hfi1); // 1
    reports(hfi2); // 2
    reports(hfdb); // 3
}

PS. Złamałeś także zasadę emisji kodu z plików nagłówkowych. Co do zasady, nagłówek nie powinien emitować kodu a dzieje się tak w counts.

komentarz 6 października 2018 przez Jakub 0 Pasjonat (23,120 p.)

Dziękuje za odpowiedź,

// NIGDY W NAGŁÓWKU!!!

//using std::cout;

//using std::endl;

Masz racje, to ma sens.

// Jeśli chcesz by counts() wiedział o funkcjach reports..., to pwinny być wimienione przed

//funkcje zaprzyjaźnione dla konkretnych konkretyzacji

To też się zgadza, jakoś nie zwróciłem na to uwagi, bo kod jak już mówiłem tak naprawdę jest przepisany z książki. Trochę słabo go przeanalizowałem.

// void main10() ? A jak program ma wejść do tej funkcji?

W treści pytania napisałem:

Ostateczne wywołanie void main10() jest oczywiście w funkcji int main().

Wygląda to tak:

#include <iostream>

void main10();

int main() { 
	main10();
	std::cin.get();
	std::cin.get();
}

Skoro się nie skarżę na to że program nic mi nie wyświetla to jakoś musi być ta funkcja wywołana ;) Ogólnie nie chciało mi się tworzyć masy nowych projektów w visual'u i dlatego tak to zrobiłem, wydawało mi się że jest to ładne rozwiązanie. Chociaż równie dobrze raczej dało by się skompilować sam plik *.cpp nie należący do żadnego projektu ( tak przynajmniej kompiluje to na linuxie w czystym terminalu )

Złamałeś także zasadę emisji kodu z plików nagłówkowych. Co do zasady, nagłówek nie powinien emitować kodu a dzieje się tak w counts.

Mógłbyś to rozszerzyć... ?

Z góry za wszystko dziękuje.

1
komentarz 6 października 2018 przez mokrowski Mędrzec (158,900 p.)
Nie wiem co tu rozszerzać? Od czego jest nagłówek a od czego *.cpp czyli plik implementacji? Odsyłam do one definition rule.

Pytania które możesz sobie postawić to ew.:

1. Jak to jest z inline?

2. Jak to jest z implementacją szablonów w nagłówku?

Na obydwa pytania znajdziesz odpowiedź w "super faq": https://isocpp.org/faq
komentarz 7 października 2018 przez Jakub 0 Pasjonat (23,120 p.)
Faktycznie, już rozumiem. Pomyliło mi się to z tym że wszelakie szablony mogą być w pliku nagłówkowym, ale counts() i reports() to przecież nie szablony...
1
komentarz 7 października 2018 przez mokrowski Mędrzec (158,900 p.)
To także mit że wszystkie szablony mają być w nagłówku. Doczytaj w faq.

Podobne pytania

93,485 zapytań

142,417 odpowiedzi

322,764 komentarzy

62,896 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

Kursy INF.02 i INF.03
...