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

wskaznik na funkcje(C++)

Object Storage Arubacloud
0 głosów
833 wizyt
pytanie zadane 6 stycznia 2020 w C i C++ przez dmnkk1 Nowicjusz (120 p.)
Czesc moglby ktos mi wytlumaczyc jak sie tworzy wskaznik na funkcje przy okazji wykonac to zadanie?

 Zaprojektować i zaimplementować funkcję roznica, która przyjmuje trzy argumenty:

• wskaźnik wsk_f na funkcję, która przyjmuje jako argument wartość rzeczywistą i która zwraca wartość rzeczywistą;

• dwie liczby rzeczywiste a i b. i zwraca wartość rzeczywistą.

Funkcja powinna:

• obliczyć wartości funkcji wsk_f dla argumentów a i b;

• zwrócić wartość bezwzględną różnicy wartości funkcji dla argumentów a i b.

2 odpowiedzi

0 głosów
odpowiedź 6 stycznia 2020 przez Ad19am94 Początkujący (440 p.)

Ten link powinien ci pomóc

1
komentarz 6 stycznia 2020 przez tkz Nałogowiec (42,000 p.)
komentarz 6 stycznia 2020 przez adrian17 Ekspert (344,860 p.)
Nie potrzebujesz std::function żeby używać wskaźniki na funkcje. std::function nie jest też obiektywnie lepsze.

Link Adama też nie idealny (mogliby użyć `using` dla czytelności), ale ogólnie jest dobry.
komentarz 6 stycznia 2020 przez tkz Nałogowiec (42,000 p.)

Również nie potrzebujesz nic z nagłówka algorithm, oraz vector, ale jest prościej i bezpieczniej. Na tym polega korzystanie z gotowych rozwiązań.  

https://stackoverflow.com/questions/25848690/should-i-use-stdfunction-or-a-function-pointer-in-c/25848890

Podaj mi obiektywne argumenty przeciw std::function.

komentarz 6 stycznia 2020 przez adrian17 Ekspert (344,860 p.)

Po pierwsze, jestem za komentarzem pod tą odpowiedzią:

"In short, use std::function unless you have a reason to" not seems a very bad default. Whatever happened to not paying for what we don't use?

Pomijając sam overhead i YAGNI, std::function jest faktycznie obiektywnie potężniejsze. Ale... to nie jest zawsze zaleta. Kod nawet obiektywnie "potężniejszy", jeśli nie używa tej dodatkowej potęgi, tylko pogarsza czytelność - bo prostszy kod łatwiej się czyta.

Na przykład: teoretycznie moglibyśmy każde wywołanie funkcji func(a, b, c) zastąpić przez std::invoke(func, a, b, c), które jest potężniejszy, a w ogóle nie wpływa na generowany kod, więc naprawdę jest obiektywnie lepszy od zwykłego wywołania funkcji, więc... dlaczego nikt tak nie robi? Bo jeśli go nie potrzebujesz, to w praktyce tylko pogarsza czytelność. Nie ma overheadu w kodzie, ale dodaje mentalny overhead przy czytaniu kodu.

BTW, jakoś osoby w tamtejszych odpowiedziach narzekające na czytelność wskaźników, nie wspominają o `using`, z którym zapis typu jest w zasadzie dokładnie taki sam (tylko piszą z konwencją C jakby to był typedef)

using Func = std::function<int(int, int)>;

// vs

using Func = int(int, int);
// opcjonalnie:
using FuncPtr = Func*;

 

komentarz 6 stycznia 2020 przez tkz Nałogowiec (42,000 p.)

Samo std::invoke jest często wykorzystywane w metaprogramowaniu. A, że zostało odkryte dopiero w C++ 17 też trochę zmienia w postrzeganiu tej funkcji. 

void foo()
{
    std::cout << "hello world\n";
};

template <bool b>
struct optionally_callable
{
    std::enable_if_t<b> operator()()
    {
        std::cout << "hi again\n";
    }
};

int main()
{
    auto c = []
    {
        std::cout << "hi from lambda\n";
    };

    std::invoke(foo);
    std::invoke(c);

    auto o = optionally_callable<true>{};
    // auto o2 = optionally_callable<false>{};

    std::invoke(o);
}

Co do overhead'u, można tak nazwać każdą rzecz, oczywiście trochę wyolbrzymiam, ale sam fakt, że przy statycznych tablicach i tak używamy vectora, bo jest po prostu łatwiej i przyjemniej. To samo tutaj, std::function zabiera całą odpowiedzialność. Jedyny powód by tego nie używać to narzut jaki tworzy. Był jeszcze z tym związany pewien idiom, ale nie mogę sobie przypomnieć jego nazwy. 

Edit: 

Type erasure https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure

komentarz 7 stycznia 2020 przez mokrowski Mędrzec (155,460 p.)

Wskaźnik na funkcję, obiekt funkcyjny (dawniej nazywany funktorem), lambda i std::function, to różne typy. Nawet 2 lambdy o takiej samej sygnaturze (argumenty przyjmowane i zwracane) to obiekty różnych typów. Aby je sprowadzić do jednego typu jakim jest std::function, użyty jest idiom wymazywania typu który podał @tkz. Może być on nieakceptowalny ze względu na koszt (najmniej 2 wywołania virtual). Stąd jeśli nie jest to konieczne do wyrażenia intencji i może być nieakceptowalne kosztowo, nie należy go "używać domyślnie bo nic nie kosztuje". Oczywiście jeśli nie ma to znaczenia (większy kod/czas wywołania/kod funkcyjny...), nie ma co kruszyć kopii :)

Byłbym ogólnie zdziwiony i chciałbym poznać powód widząc std::function w klasycznym embedded. Ale już w aplikacji głównego nurtu np z GUI, czemu nie. Tylko zawsze wtedy sprawdził bym czy naprawdę nie da się podstawić jakiegoś typu wspólnego i na nim zrobić wskaźnik. To zawsze będzie szybsze a dewirtualzacja Type Erasure, to pokładanie nadziei w konkretnej implementacji kompilatora. Czyli może będzie ale raczej nie :-/ Jak się da rozwiązać problem na etapie kompilacji (np. CRTP), to lepiej to zrobić tu a nie w runtime. A std::function rozwiązuje to w runtime.

Co do czytelności wskaźnika na funkcję. Raczej nie widzę problemu. Problem tylko widzę zrozumieniu przez wszystkich w zespole wskaźnika na metodę w klasie. To już "nie każdy ogarnia" :) Z całą pewnością wskaźnika na funkcję nie tpedef'ował bym. Zastanawiał bym się nad wskaźnikiem na metodę w klasie. Ale to już zależy od kontekstu.

0 głosów
odpowiedź 7 stycznia 2020 przez mokrowski Mędrzec (155,460 p.)

Czesc moglby ktos mi wytlumaczyc jak sie tworzy wskaznik na funkcje przy okazji wykonac to zadanie?

Wytłumaczyć, mogę spróbować. Zrobić zadanie nie.

Jeśli masz funkcję, to jej kod rezyduje gdzieś w pamięci. Możesz uzyskać do niej dostęp poprzez wskaźnik który jest jej adresem. Z racji tego że w C jak i w C++ taki wskaźnik jest traktowany jak sama funkcja, będziesz mógł ją zawołać bezpośrednio przekazując argumenty i uzyskać wynik.

Ważna sprawa. Wskaźnik na funkcję, jest inną kategorią wskaźnika niż wskaźnik na dane/zmienną/obiekt. Nie wolno Ci rzutować z jednego wskaźnika inny  rodzaj (wskaźnik na "nie funkcję") bo zawsze będziesz miał zachowanie niezdefiniowane

Taki wskaźnik tworzy się bardzo prosto:

#include <iostream>

int calc(int a, int b) {
    return a + b;
}

int main() {
    // Wskaźnik o nazwie funPtr na funkcję zwracającą int i przyjmującą dwa arg. typu int
    int (*funPtr)(int, int) = calc;
    // Nie jest konieczne dopisywanie & przed nazwą funkcji np. tak:
    // int (*funPtr)(int, int) = &calc;

    // Wywołanie funkcji
    int result = funPtr(4, 5);
    // Znów, nie jest konieczne stawianie wyłuskania takiego wskaźnika np. tak:
    // int result = (*funPtr)(4, 5);

    std::cout << result << '\n';
}

Jeśli chcesz przesłać dany wskaźnik na funkcję do innej funkcji, to wystarczy to zrobić tak:

#include <iostream>

int calc(int a, int b) {
    return a + b;
}

void showCalc(int (*fun)(int, int), int arg1, int arg2) {
    std::cout << fun(arg1, arg2) << '\n';
}

int main() {
    showCalc(calc, 4, 5);
}

Jeśli z jakiegoś powodu (np. funkcja ma wiele argumentów, ma rozbudowany typ danych zwracany...), nie pasuje Ci taki zapis wskaźnika na funkcję, możesz do niej dodać inną nazwę która nie jest niczym innym niż tylko innym nazwaniem wskaźnika na funkcję. Taka nazwa nie tworzy innego bytu a jedynie ułatwia zapis:

#include <iostream>

using calcPtr = int(*)(int, int);

int calc(int a, int b) {
    return a + b;
}

void showCalc(calcPtr fun, int arg1, int arg2) {
    std::cout << fun(arg1, arg2) << '\n';
}

int main() {
    showCalc(calc, 4, 5);
}

To Ci wystarczy do rozwiązania zadania plus wiedza o tym że jest funkcja zwracająca wartość bezwzględną std::abs(...).

Podobne pytania

0 głosów
1 odpowiedź 92 wizyt
0 głosów
1 odpowiedź 150 wizyt
pytanie zadane 22 stycznia 2020 w C i C++ przez amtrax Dyskutant (9,630 p.)
0 głosów
1 odpowiedź 137 wizyt
pytanie zadane 19 marca 2019 w C i C++ przez Poczprogramista123 Bywalec (2,900 p.)

92,568 zapytań

141,424 odpowiedzi

319,632 komentarzy

61,956 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!

...