Uwaga: nie chodzi o to, jak używać funkcji inline ani jak one działają, a raczej o to, dlaczego są robione tak, jak są.
Deklaracja funkcji składowej klasy nie musi definiować funkcji inline
, ponieważ jest to tylko faktyczna implementacja funkcji. Na przykład w pliku nagłówkowym:
struct foo{
void bar(); // no need to define this as inline
}
Dlaczego więc wbudowana implementacja funkcji klas musi znajdować się w pliku nagłówkowym? Dlaczego nie mogę umieścić funkcji wbudowanej w .cpp
pliku? Gdybym próbował umieścić definicję wbudowaną w .cpp
pliku, wystąpiłby błąd w następujący sposób:
error LNK2019: unresolved external symbol
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe
: fatal error LNK1120: 1 unresolved externals
inline
pojawia się w definicji, ale nie we wcześniejszej deklaracji, a odwrotnie . Jeśli tak, to może pomóc: stackoverflow.com/questions/4924912/…Odpowiedzi:
Definicja
inline
funkcji nie musi znajdować się w pliku nagłówkowym, ale ze względu na regułę jednej definicji ( ODR ) dla funkcji wbudowanych, identyczna definicja funkcji musi istnieć w każdej jednostce tłumaczeniowej, która jej używa.Najłatwiejszym sposobem osiągnięcia tego jest umieszczenie definicji w pliku nagłówkowym.
Jeśli chcesz umieścić definicję funkcji w jednym pliku źródłowym, nie powinieneś jej deklarować
inline
. Nie zadeklarowana funkcja nieinline
oznacza, że kompilator nie może wbudować funkcji.To, czy powinieneś zadeklarować funkcję,
inline
czy nie, jest zwykle wyborem, którego powinieneś dokonać w oparciu o wersję jednej definicji reguł, która jest dla ciebie najbardziej sensowna; dodawanie,inline
a następnie ograniczanie przez kolejne ograniczenia nie ma sensu.źródło
+1
ode mnie!Można na to spojrzeć na dwa sposoby:
Funkcje wbudowane są zdefiniowane w nagłówku, ponieważ aby wbudować wywołanie funkcji, kompilator musi mieć możliwość zobaczenia treści funkcji. Aby naiwny kompilator to zrobił, treść funkcji musi znajdować się w tej samej jednostce tłumaczenia, co wywołanie. (Nowoczesny kompilator może optymalizować w jednostkach tłumaczeniowych, więc wywołanie funkcji może być wbudowane, nawet jeśli definicja funkcji znajduje się w oddzielnej jednostce tłumaczeniowej, ale te optymalizacje są drogie, nie zawsze są włączone i nie zawsze były obsługiwane przez kompilator)
funkcje zdefiniowane w nagłówku muszą być zaznaczone,
inline
ponieważ w przeciwnym razie każda jednostka translacji, która zawiera nagłówek, będzie zawierała definicję funkcji, a linker będzie narzekał na wiele definicji (naruszenie reguły jednej definicji).inline
Kluczowe tłumi tego, pozwalając wielu jednostek tłumaczenie zawierać (identyczne) definicje.Te dwa wyjaśnienia tak naprawdę sprowadzają się do tego, że
inline
słowo kluczowe nie robi dokładnie tego, czego można się spodziewać.Kompilator C ++ może zastosować optymalizację wbudowaną (zastąpić wywołanie funkcji ciałem wywoływanej funkcji, oszczędzając narzut wywołania) w dowolnym momencie, o ile nie zmienia obserwowalnego zachowania programu.
Słowo
inline
kluczowe ułatwia kompilatorowi zastosowanie tej optymalizacji, umożliwiając wyświetlanie definicji funkcji w wielu jednostkach tłumaczeniowych, ale użycie słowa kluczowego nie oznacza, że kompilator musi wbudować funkcję, a nie użycie słowa kluczowego nie. Zabroń kompilatorowi wstawiania funkcji.źródło
To jest ograniczenie kompilatora C ++. Jeśli umieścisz funkcję w nagłówku, wszystkie pliki cpp, w których można ją wstawić, będą widzieć „źródło” Twojej funkcji, a kompilator będzie mógł wykonać wstawianie. W przeciwnym razie wstawianie musiało być wykonane przez konsolidator (każdy plik cpp jest kompilowany osobno w pliku obj). Problem w tym, że byłoby to dużo trudniejsze do zrobienia w linkerze. Podobny problem występuje w przypadku klas / funkcji „szablonów”. Muszą zostać utworzone przez kompilator, ponieważ konsolidator miałby problem z ich utworzeniem (utworzeniem specjalistycznej wersji). Niektóre nowsze kompilatory / konsolidatory mogą wykonywać kompilację / linkowanie „dwuprzebiegowe”, gdzie kompilator wykonuje pierwszy przebieg, a następnie konsolidator wykonuje swoją pracę i wywołuje kompilator w celu rozwiązania nierozwiązanych rzeczy (inline / templates ...)
źródło
Powodem jest to, że kompilator musi faktycznie zobaczyć definicję , aby móc ją upuścić w miejsce wywołania.
Pamiętaj, że C i C ++ używają bardzo uproszczonego modelu kompilacji, w którym kompilator zawsze widzi tylko jedną jednostkę tłumaczeniową na raz. (Nie udaje się to w przypadku eksportu, co jest głównym powodem, dla którego tylko jeden dostawca faktycznie go wdrożył).
źródło
Słowo
inline
kluczowe c ++ jest mylące, nie oznacza „wbudowanej tej funkcji”. Jeśli funkcja jest zdefiniowana jako inline, oznacza to po prostu, że można ją zdefiniować wiele razy, o ile wszystkie definicje są równe. Funkcja oznaczonainline
jako prawdziwa funkcja, która jest wywoływana zamiast pobierania kodu w miejscu, w którym jest wywoływana, jest całkowicie legalna .Definiowanie funkcji w pliku nagłówkowym jest potrzebne w przypadku szablonów, ponieważ np. Klasa z szablonem nie jest tak naprawdę klasą, jest szablonem dla klasy, której można dokonywać w wielu odmianach. Aby kompilator mógł np. Utworzyć
Foo<int>::bar()
funkcję, gdy używasz szablonu Foo do tworzenia klasy Foo , rzeczywista definicjaFoo<T>::bar()
musi być widoczna.źródło
inline
(ani nie zadeklarowanie jejinline
gwarantuje, że nie zostanie ona wstawiona).Wiem, że to stary wątek, ale pomyślałem, że powinienem wspomnieć o tym
extern
słowie kluczowym. Niedawno napotkałem ten problem i rozwiązałem go w następujący sposóbHelper.h
Helper.cpp
źródło
Ponieważ kompilator musi je zobaczyć, aby je wstawić . Pliki nagłówkowe to „komponenty”, które są zwykle zawarte w innych jednostkach tłumaczeniowych.
źródło
Funkcje wbudowane
W C ++ makro to nic innego jak funkcja inline. Więc teraz makra są pod kontrolą kompilatora.
Kod funkcji Inline jest zastępowany w miejscu jej wywołania, co zmniejsza narzut wywołania funkcji.
W niektórych przypadkach inlining funkcji nie może działać, na przykład
Jeśli zmienna statyczna jest używana wewnątrz funkcji inline.
Jeśli funkcja jest skomplikowana.
Jeśli rekurencyjne wywołanie funkcji
Jeśli adres funkcji przyjęty niejawnie lub jawnie
Funkcja zdefiniowana poza klasą, jak poniżej, może stać się wbudowana
Funkcje zdefiniowane w klasie również stają się wbudowane
Tutaj obie funkcje getSpeed i setSpeed staną się wbudowane
źródło