Problem z STL -> for_each, mem_fun

Problem z STL -> for_each, mem_fun
43
  • Rejestracja:prawie 14 lat
  • Ostatnio:ponad 6 lat
  • Postów:61
0

Witam!

Na wstępię zaznaczę, że mam pewne istotne braki z filozofii STLa i typów generycznych w C++.

Chciałbym podać referencję do "member function", która później byłaby użyta w for_each.

Kod prezentujący mój problem:

Kopiuj
#include <functional>
#include <vector>
#include <algorithm>
#include <iostream>


class container {
	std::vector<int> vect; // [ 0, 1, 2, 3, 4 ]

	public:
		container() {
			for (int i = 0; i < 5; i++ )
				vect.push_back(i);
		}

		template<class Function>
			void each (Function f) {
				for_each(vect.begin(), vect.end(), f);
			}
} o_container ;

class caller {
	public:
		void funkcja(int i) { std::cout << i; }
		caller() {
			o_container.each ( std::mem_fun(funkcja) ); // Ten kod generuje błąd-> argument of type 'void (caller::)(int)' does not match 'void (caller::*)(int)' pod MinGW
		}
} o_caller;

int main() {

	return 0;
}

 

Ja już tego po prostu nie ogarniam, a google i inne szukaji nie pomagają ;(

Z góry dziękuję za poświęcenie chwilki czasu!

edytowany 1x, ostatnio: 4ggr35510n
rincewind
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
0

Na pierwszy rzut oka: potrzebujesz adresu do member function, dlatego też w wywołaniu std::mem_fun() powinieneś podać pełną specyfikację, łącznie z klasą:

Kopiuj
std::mem_fun(&caller::funkcja)

Edit Patrząc na komunikat błędu:

argument of type void (caller::)(int) does not match void (caller::*)(int)

można wywnioskować, że zadziałałoby nawet bez specyfikacji typu klasy, brakuje jedynie operatora adresu &. Ale to domysły. Poczytaj więcej tutaj, szczególnie example: http://www.cplusplus.com/reference/std/functional/mem_fun/


edytowany 1x, ostatnio: rincewind
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

Na drugi rzut oka "funkcja" ma de facto dwa argumenty (caller *, int), więc należałoby to jakoś uwzględnić.
Albo użyć "static" - co pewnie nie wchodzi w rachubę, albo pokombinować...

Przykład ze strony boost:

Kopiuj
boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

http://www.boost.org/doc/libs/1_46_1/doc/html/function/tutorial.html

rincewind
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
0

OK, dzięki, vpiotr. Rzeczywiście, mem_fun() zwraca funkcję dwuargumentową, gdzie pierwszym argumentem jest adres obiektu, na rzecz którego wywołana będzie metoda klasy. Dlatego należy zmienić wywołanie o_container.each() w taki sposób:

Kopiuj
caller() {
	o_container.each(std::bind1st(std::mem_fun(&caller::funkcja), this));
}

W ten sposób bindujemy pierwszy argument na obiekt callera, który właśnie jest w użyciu (this).


edytowany 1x, ostatnio: rincewind
43
  • Rejestracja:prawie 14 lat
  • Ostatnio:ponad 6 lat
  • Postów:61
0

Dziękuję wam za odpowiedzi!

Poprawka rincewinda sprawia, iż poniższy kod działa poprawnie:

Kopiuj
 #include <functional>
#include <vector>
#include <algorithm>
#include <iostream>


class container {
	public:
		std::vector<int> vect; // [ 0, 1, 2, 3, 4 ]

	public:
		container() {
			for (int i = 0; i < 5; i++ )
				vect.push_back(i);
		}

		template<class Function>
			void each (Function f) {
				for_each(vect.begin(), vect.end(), f);
				std::cout << "\n";
			}
} o_container ;

class caller {
	public:
		void funkcja(int i) { std::cout << i; }
		void funkcja_ref(int& j ) { std::cout << j; }
		caller() {
			o_container.each ( std::bind1st( std::mem_fun(&caller::funkcja), this)  );

//			o_container.each ( std::bind1st( std::mem_fun_ref(&caller::funkcja_ref), this)  );	// Kompilator wyrzuca tonę błędów wskazując na STLa.

			std::cout << "\n";
		}
} o_caller;

int main() {
	return 0;
}

Mam jednak kolejny problem, zamieszczony w powyższym kodzie. Staram się w podobny sposób wywołać funkcję przekazującą argument przez referencję. Służyć do tego powinno mem_fun_ref z STLa.

W najprzeróżniejszych jednak kombinacjach nie działa - specyfikacja na cplusplus.com tłumaczy ten temat straszliwie po łebkach, nie jestem w stanie nigdzie znaleźć rozwiązania.

Wszystkie błędy kierują mnie do bibliotek STLa: stl_algo.h, binders.h etc. - nie mam więc żadnej wskazówki o co może chodzić.

Z góry dziękuję za poświęcony czas :)

edytowany 1x, ostatnio: 4ggr35510n
rincewind
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
1

Krótko: źle rozumiesz działanie mem_fun_ref(). Nie wpływa on na sposób przekazywania parametrów, nieważne, czy przesyłasz je przez wartość, referencję czy wskaźnik. W Twoim przypadku mem_fun_ref() nie ma żadnego zastosowania.

Dłużej: mem_fun() zwraca funktor, który jako pierwszego parametru oczekuje wskaźnika na obiekt klasy, do którego metody linkuje. Z kolei mem_fun_ref() zwraca funktor, którego pierwszym parametrem jest referencja do obiektu. Wydaje się to niewielką różnicą, ale w praktyce jest dość istotne. Szczególnie w funkcjach typu transform(), for_each() i innych. Przykład z cplusplus.com pokazuje te różnice:

mem_fun():

Kopiuj
vector<string*> numbers; // zauwaz, ze wektor przechowuje *wskazniki* na string
vector<int> lengths(numbers.size());
// ...
transform(numbers.begin(), numbers.end(), lengths.begin(), mem_fun(&string::length)); // dlatego tutaj mem_fun() dziala, bo oczekuje wlasnie wskaznika do obiektu jako parametru

mem_fun_ref():

Kopiuj
vector<string> numbers; // ...a tutaj wskaznikow nie ma
vector<int> lengths(numbers.size());
// ...
transform(numbers.begin(), numbers.end(), lengths.begin(), mem_fun_ref(&string::length)); // wiec tutaj potrzeba funktora pobierajacego referencje do obiektu

Sprawdź też tutaj: http://www.richelbilderbeek.nl/CppMem_fun_ref.htm


edytowany 1x, ostatnio: rincewind
43
  • Rejestracja:prawie 14 lat
  • Ostatnio:ponad 6 lat
  • Postów:61
0

Dzięki za odpowiedź :}

Jednak jeśli mem_fun oczekuje wskaźnika na obiekt, dlaczego kod zamieszczony powyżej działa?

Kopiuj
o_container.each ( std::bind1st( std::mem_fun(&caller::funkcja), this)  );

Wszak container::vect przechowuje <int>, nie <int*>.

Zaś wersja z mem_fun_ref zmusić do działania nie sposób, mimo, iż wskaźników nigdzie nie przechowuję.

Również nie sposób zmusić do działania funkcji funkcja_ref, pobierającej referencję na int'a (int&).

Niezależnie od tego, czy korzystam z bind1st (którego, notabene, działania tutaj zupełnie nie rozumiem -> czym jest pierwszy argument funkcji zwracanej przez mem_fun?), czy nie, czy z mem_fun czy z mem_fun_ref -> przekazać int'a przez referencję nie sposób...

Kopiuj
#include <functional>
#include <vector>
#include <algorithm>
#include <iostream>


class container {
	public:
		std::vector<int> vect; // [ 0, 1, 2, 3, 4 ]

	public:
		container() {
			for (int i = 0; i < 5; i++ )
				vect.push_back(i);
		}

		template<class Function>
			void each (Function f) {
				for_each(vect.begin(), vect.end(), f);
				std::cout << "\n";
			}
} o_container ;

class caller {
	public:
		void funkcja(int i) { std::cout << i; }
		void funkcja_ref(int& j ) { std::cout << j; }
		caller() {
			// działa w porządku...
			o_container.each ( std::bind1st( std::mem_fun(&caller::funkcja), this)  );

			// nie działa zupełnie... W każdym przypadku zwraca błędy biblioteki STLa (stl_algo.h, binders.h, etc.)
			// o_container.each ( std::bind1st( std::mem_fun_ref(&caller::funkcja_ref), this)  );
			// o_container.each ( std::mem_fun_ref(&caller::funkcja_ref) );
			// o_container.each ( std::bind1st( std::mem_fun_ref(&caller::funkcja_ref), this)  );
			// o_container.each ( std::mem_fun(&caller::funkcja_ref)  );

			std::cout << "\n";
		}
} o_caller;

int main() {
	return 0;
}

edytowany 1x, ostatnio: 4ggr35510n
Endrju
  • Rejestracja:około 22 lata
  • Ostatnio:ponad rok
0
Kopiuj
o_container.each(std::bind1st(std::mem_fun(&caller::funkcja_ref), this));

Tak powinno działać, po prostu. Ale nie zadziała, bo w bibliotece standardowej jest błąd (błąd w specyfikacji) - mem_fun próbuje zrobić referencję do referencji. Możesz użyć albo wskaźnika, albo nowych funkcji std::bind z C++11.


"(...) otherwise, the behavior is undefined".
edytowany 1x, ostatnio: Endrju
rincewind
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
1

[1] Funkcja z parametrem jako referencja -- po chwili googlania: http://stackoverflow.com/questions/5349973/why-did-it-compile-errorly-when-i-added-a-reference-argument-to-a-member-functio -- tutaj czytamy:

The problem is that internally, mem_fun tries to set as its argument type a const reference to the argument type of the member function. If you make then function take in a reference, then it tries to create a reference to a reference, which is illegal in C++.

Czyli nie da się poprzez mem_fun() ani mem_fun_ref() przekazać parametru jako referencji. To samo wewnętrznie robi mem_fun(), co powoduje próbę stworzenia referencji do referencji, co jest zabronione.

[2] Działanie funktorów, bindowania, etc: popatrz na to tak. Masz wskaźnik na metodę klasy (w Twoim przypadku caller::funkcja(int)). Metody są związane z obiektem, nie z klasą, dlatego potrzebny jest kontekst jej wywołania. Wobec tego:

Kopiuj
void A::foo(T1, T2, ...) -> void F(T1, T2, ...)
Robimy mem_fun():
mem_fun(&A::foo) -> void F(caller*, T1, T2, ...)

Jak widać. mem_fun() dodaje na pierwszej pozycji w liście parametrów wskaźnik do kontekstu wywołania metody, czyli obiektu, na rzecz którego metoda zostanie wywołana.

W przykładach na cplusplus.com pokazane jest, jak używać metod mem_fun() i mem_fun_ref() na elementach przechowywanych w kontenerze:

Kopiuj
vector<string> numbers;
vector<int> lengths;
// ...
transform(numbers.begin(), numbers.end(), lengths.begin(), mem_fun_ref(&string::length));

Przekazywana jest metoda klasy string, operuje się też na wektorze string. W tym wypadku dla każdego elementu w numbers wywołana zostanie metoda length() i dopisana do wektora lengths. Wszystko łatwe i przyjemne.

teraz: po co bind1st()? Najpierw -- czym jest bindowanie argumentów (właściwie od początku posługujemy się tu terminami związanymi z programowaniem funkcyjnym, warto wiedzieć). Bindowanie ustala jeden z argumentów funkcji na jakąś stałą wartość, powodując zmniejszenie liczby parametrów. Kilka przykładów:

Kopiuj
bind1st(void F(int, double, string), 666) -> void F(double, string) // "bind first" -- pierwszy parametr funkcji ustalony na 666, zwracana jest funkcja dwuargumentowa
bind2nd(void F(int, double, string), 6.66) -> void F(int, string) // "bind second" -- drugi parametr funkcji ustalony na 6.66, zwracana jest funkcja dwuargumentowa
bind3rd(void F(int, double, string), "Devil") -> void F(int, double) // "bind third" -- trzeci parametr funkcji ustalony na "Devil", zwracana jest funkcja dwuargumentowa

Jeszcze raz -- po co to? Ano dlatego, że w Twoim przypadku do for_each() trafia metoda klasy, która nie jest przechowywana w kontenerze. Wobec tego należy nadać jej właściwy kontekst. Tym kontekstem jest wskaźnik na obiekt callera. Jako, że w momencie wywoływania metody container::each() znajdujemy się w konstruktorze o_caller, więc bindujemy metodę mem_ref(&caller::funkcja) z wskaźnikiem na obiekt callera -- czyli this. W ten sposób do metody container::each() wysyłamy funkcję jednoargumentową, i for_each() nie będzie próbował bindować pierwszego parametru na aktualnie przetwarzany obiekt z kontenera.

[3] Różnica między mem_fun() a mem_fun_ref():

Kopiuj
mem_fun(void A::foo(T1, T2, ...)) -> void F(A*, T1, T2, ...)
mem_fun_ref(void A::foo(T1, T2, ...)) -> void F(A&, T1, T2, ...)

Do bindowania "ręcznego", jak w Twoim przypadku, mem_fun_ref() się nie nadaje z tego samego powodu, z którego nie można przesyłać parametrów przez referencję. Znajduje jednak zastosowanie w funkcjach bibliotecznych, a różnicę w zastosowaniu mem_fun() i mem_fun_ref() można zobaczyć na cplusplus.com.


Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)