2010-03-23 18 views
18

Jeśli zdefiniujemy funkcję składową w samej definicji klasy, czy jest ona traktowana inline, czy jest to po prostu żądanie kompilatora, które może zignorować.Funkcje wbudowane w C++

+0

Zobacz także http://stackoverflow.com/questions/908830/isnt-cs-inline-totally-optional/910686#910686 –

Odpowiedz

18

Tak, funkcje zdefiniowane w treści klasy są domyślnie inline.

(Podobnie jak w przypadku innych funkcji zadeklarowanych jako inline nie oznacza to, że kompilator musi wykonywać liniowe rozszerzanie w miejscach, w których funkcja jest wywoływana, tylko zezwala na dozwolone rozluźnienia "jednej reguły definicji" w połączeniu z wymaganiem że definicja musi być zawarta we wszystkich jednostkach tłumaczeniowych, w których funkcja jest używana.)

2

Jest to żądanie do kompilatora, które może zignorować.

+4

Nie, jest to wymagane. Jeśli funkcja jest zdefiniowana w treści klasy, musi być traktowana tak, jakby została zadeklarowana jako "inline". –

+5

podobnie jak każde żądanie "inline", które kompilator może zignorować. – shoosh

+1

Ponieważ można go zignorować, a większość kompilatorów będzie wstawiać tam, gdzie jest to właściwe, nawet gdy nie zażąda się go, jest to dość bezużyteczna wskazówka. – marr75

3

Jest ona traktowana przez kompilator jako żądanie do wstawienia - które może zignorować. Istnieją pewne idiomy do definiowania niektórych funkcji w nagłówku (np. Puste destruktory wirtualne) i niektóre niezbędne definicje nagłówków (funkcje szablonów), ale inne niż te zobacz GotW #33, aby uzyskać więcej informacji.

Niektórzy zauważyli, że kompilator może nawet wstawiać funkcje, o które nigdy nie prosiłeś, ale nie jestem pewien, czy to by uniemożliwiło zadanie wywołania funkcji.

+0

Zależy od kompilatora. Generalnie inline to podpowiedź, która niewiele robi. Myślę, że większość kompilatorów ukierunkowanych na platformy osadzone słucha podpowiedzi ściślej, a to jest naprawdę jedyna przestrzeń, w której po starannym zaprojektowaniu może być lepiej niż kompilator. – marr75

3

Jest rzeczywiście wstawiony - ale dowolne wbudowane żądanie może zostać zignorowane przez kompilator.

2

Norma ISO C++ 2003 mówi

7.1.2/2 Deklaracja funkcji (8.3.5, 9.3, 11.4) z inline specyfikatorem deklaruje inline funkcję. Inline specyfikator wskazuje realizacji że rolki podstawienie treść funkcji w miejscu połączenia ma być korzystne zwykłe wywołanie funkcji
mechanizmu. Implementacja nie jest wymagana w celu dokonania tego zastępowania w miejscu połączenia; jednakże, nawet jeśli to podstawienie zostanie pominięte, pozostałe zasady dla funkcji wbudowanych zdefiniowanych jako według 7.1.2 będą nadal respektowane.

7.1.2/3 Funkcja zdefiniowana w definicji klasy jest wbudowaną funkcją
. Wewnętrzny specyfikator nie może pojawić się w deklaracji funkcji zakresu blokowego .

7.1.2/4 Funkcja inline określa się w każdej jednostce tłumaczeniowej w
który jest używany i ma dokładnie taką samą definicję w każdym
przypadku (3.2). [Uwaga: wywołanie funkcji inline może zostać napotkane przed jej definicją w jednostce tłumaczeniowej . ] Jeżeli funkcja z zewnętrznym połączeniem jest zadeklarowana jako w jednej jednostce przeliczeniowej, to musi ona być deklarowana we wszystkich jednostkach tłumaczeniowych , w której występuje ; diagnostyka nie jest wymagana.Funkcja inline z zewnętrznym łącznikiem musi mieć ten sam adres w jednostkach tłumaczeniowych pod numerem . Statyczna zmienna lokalna w zewnętrznej funkcji wbudowanej zawsze odnosi się do tego samego obiektu . Literał ciągu znaków w zewnętrznej funkcji inline jest tym samym obiektem w różnych jednostkach translacji.

+0

Jest to najbardziej przydatna odpowiedź, ale jest również myląca, ogólnie rzecz biorąc, główny nurt kompilatorów koncentruje się na "elemencie implementacji nie wymaga tego elementu zastępującego" standardu. – marr75

1

Są dwie rzeczy, które nie powinny być zalicza razem:

  1. Jak zaznaczyć funkcję jako inline: zdefiniować go inline przed podpisaniem lub zdefiniować ją w punkcie deklaracji;
  2. Co kompilator będzie obsługiwał takie wbudowane znakowanie: niezależnie od tego, jak oznaczono funkcję jako wbudowaną, będzie traktowane jako żądanie przez kompilator.
4

Jak podano przez innych, metoda zdefiniowana w klasie jest automatycznie żądana w linii. Warto zrozumieć, dlaczego.

Załóżmy, że tak nie było. Będziesz musiał wygenerować kod dla takiej funkcji, a wszędzie, gdzie jest ona wywoływana, skok do instrukcji podprogramu będzie musiał odwoływać się do lokalizacji za pośrednictwem łącznika.

class A { 
public: 
    void f() { ... your code ... } 
}; 

Za każdym razem ten kod jest widziany, jeśli nie jest inline, kompilator może przyjmować tylko musi zostać wygenerowany, więc byłoby wygenerować symbol. Załóżmy, że to było tak:

A__f_v:

Jeśli symbol były globalne, a następnie, jeśli stało się to ten kod klasy wiele razy w różnych modułach, to miałby wielokrotnie zdefiniowany symbol błędu w czasie połączenia. Więc nie może być globalne. Zamiast tego jest to plik lokalny.

Wyobraź sobie, że umieściłeś powyższy plik nagłówkowy w wielu modułach. W każdym z nich wygeneruje lokalną kopię tego kodu. Co jest lepsze niż brak kompilacji, ale dostajesz wiele kopii kodu, kiedy naprawdę potrzebujesz tylko jednego.

Prowadzi to do następującego wniosku: jeśli twój kompilator nie będzie wbudowywał funkcji, znacznie lepiej jest go zadeklarować gdzieś na raz i nie żądać, aby był on wstawiony.

Niestety to, co jest i nie jest wbudowane, nie jest przenośne. Jest określony przez autora kompilatora. Dobrą zasadą jest zawsze tworzyć każdą linijkę, w szczególności wszystkie funkcje, które same nazywają funkcję, wbudowane, gdy usuniesz obciążenie. Wszystko poniżej trzech linii liniowego kodu jest prawie na pewno w porządku. Ale jeśli masz pętlę w kodzie, pytanie brzmi, czy kompilator zezwoli na to w linii, a bardziej do rzeczy, ile korzyści zobaczyłbyś, nawet gdyby zrobił to, co chcesz.

rozważyć ten kod inline:

inline int add(int a, int b) { return a + b; } 

To nie tylko prawie tak małe jak prototyp będzie w kodzie źródłowym, ale asemblera generowane przez kod inline jest mniejszy niż wezwanie do rutynowych byłoby . Więc ten kod jest mniejszy i szybszy.

I, jeśli zdarzy ci się być przechodzącej w stałych:

int c= add(5,4); 

To rozwiązany w czasie kompilacji i nie ma kodu.

W gcc, niedawno zauważyłem, że nawet jeśli nie mam kodu inline, czy to lokalny do pliku, będą one sneakily inline tak. Dopiero jeśli zadeklaruję funkcję w osobnym module źródłowym, nie zoptymalizuję połączenia.

Na drugim końcu spektrum, załóżmy poprosić inline na kawałku 1000 linii kodu. Nawet jeśli kompilator jest na tyle głupi, aby przejść wraz z nim, jedyną rzeczą, jaką można zapisać to samo wezwanie, a koszt jest to, że za każdym razem to nazwać, kompilator musi przenieść cały ten kod. Jeśli tak nazwać razy N Kod twój kod rośnie o rozmiar rutyny * n. Zatem wszystko, co jest większe niż 10 linii, nie jest warte podkreślenia, z wyjątkiem specjalnego przypadku, w którym jest on nazywany bardzo małą liczbą razy. Przykładem może być metoda prywatna zwana tylko przez 2 inne.

Jeśli żądać inline metodę zawierającą pętlę, tylko ma sens, jeśli często wykonuje niewielką liczbę razy. Ale pomyśl o pętli, która iteruje milion razy. Nawet jeśli kod jest wstawiony, procent czasu spędzonego na rozmowie jest niewielki. Więc jeśli masz metod z pętli w nim, które wydają się być większe i tak, te są warte usunięcie z pliku nagłówka, ponieważ a) będą miały tendencję zostać odrzucony jako inline przez kompilator i b), nawet jeśli zostały one wstawiane są na ogół nie zapewni żadnych korzyści.

+0

Po prostu rozwaliłeś mój umysł. Jeśli zdefiniuję funkcję w normalny sposób (z definicją tutaj i implementacją tam na dole), a ja wstawię słowo kluczowe z przodu (definicji implementacji? Obojga?), To kompilator rozważy jego wstawienie? A to jest lepsze? Moją główną obawą związaną z funkcjami wbudowanymi jest to, że są brzydkie i powodują, że linia jest długa. Czy mówisz, że mogę mieć normalnie seksowne i łatwe do odczytania funkcje wbudowane ... i nie tylko to, ale czy w ten sposób zaznaczę małe funkcje? Mam nadzieję, że jesteś aktywnym użytkownikiem i odpowiesz na ten komentarz. – Ziggy

+1

Jeśli zdefiniujesz funkcję za pomocą prototypu, z implementacją w tym samym pliku nagłówkowym zaznaczonym w wierszu, zostanie ona wstawiona jako wbudowana. Jeśli implementacja znajduje się w innym pliku, kompilator nie może go wstawić. To nie jest czytnik myśli, nie wie, jaki kod wygenerować. Osobiście nie mam nic przeciwko dodatkowemu kodowi w nagłówku, zamiast pisać go dwa razy, ale na pewno możesz to zrobić, jeśli wolisz. – Dov

+1

@ziggy, jeśli dobrze rozumiem twoje pytanie, tak, jeśli twoja funkcja jest krótka, zdefiniowanie jej w wierszu w pliku nagłówkowym może być ogromną wygraną w czasie runtime, jednocześnie nieco spowalniając czas kompilacji, a także wymagając wszelkich symboli wymienionych w zdefiniować funkcję inline. Oznacza to, że jeśli twój kod śródliniowy odnosi się do innych obiektów, twój nagłówek może zawierać inne nagłówki. Tak więc istnieje potencjalne duże koszty w czasie rekompilacji. Ale dla średniej metody (getter/setter, obliczanie czegoś ze zmiennymi prywatnymi), inlineing to ogromna wygrana. – Dov

Powiązane problemy