[C++] Funkcja inline a typ linkage'u

cześć wam,

mam pytanie odnośnie funkcji inline a ich typu linkage. W standardzie C++ jest napisane (7.1.1.7):

Jak widać wg tego, funkcja inline ma external linkage.

A dlaczego nie internal linkage :?: Wg mnie powinno tak być z 2 powodow:

- dzięki external linkage możemy korzystać z funkcji w 2 albo więcej plikach. Jeżeli kompilator rzeczywiście spełni prośbę z inline (czasem może nie spełnić) to ta funkcja zostanie wklejona w miejsce wywołań, a sama zniknie - dzieki temu nie będzie widoczna w drugim pliku i linker wyświetli błąd .

- nawet jakby linker tej prośby nie spełnił i funkcja nie będzie inline, to to słowo dalej zostanie. Dla osób nie wiedzących że inline jest external linkage, będzie wydawał się dziwny taki zapis z funkcją inline 2 dwoch plikach

Co o tym sądzicie :?:

niemożliwe - nie możesz używać funkcji w jednostce translacji, w której nie ma deklaracji funkcji (np. z nagłówka). inline, natomiast, wymusza wtedy użycie pełnej definicji (w każdym miejscu identycznej) funkcji, więc siłą rzeczy - opisana sytuacja nie może mieć miejsca.

EDIT: rozróżnienie http://bytes.com/topic/c/answers/462342-static-inline-functions

  1. Nie musi być w każdym pliku identyczna (sprawdzone w g++).

Ale to fakt - definicja musi być w każdym pliku. Jeżeli w jednym dam deklarację a w drugim definicję to w tym pierwszym wyświetli się błąd o nieznalezieniu funkcji.

Więc jak w końcu jest z tym ??? !!

Polecam ISO/IEC 14882:2003 (7.1.2.4):

Co więcej, sprawdź sobie taki kod:

// file: def.h

#ifndef _DEF_H_

#define _DEF_H_


inline /* static */ int foo() // mozliwy wariant ze static

{

  static int i = 0;

  return i++;

}

#endif

// file: bar.cpp


//#include "def.h"


inline int foo() // wykomentuj i odkomentuj wyzej, aby uzywac starej definicji foo()

{

  static int i = 0;

  return i--;

}


int bar()

{

  return foo();

}

// file main.cpp


#include 

#include "def.h"


extern int bar();


int main()

{

  for (int i = 0; i < 5; ++i)

    {

      foo();

      bar();

    }


  std::cout << foo() << std::endl;

  std::cout << bar() << std::endl;


  return 0;

}

kompiluj za pomocą:

g++ -Wall -pedantic main.cpp bar.cpp -o main

Prześledź różne warianty. Rezultat jest ciekawy w przypadku, kiedy g++ “dopuszcza” różne definicje foo() (kiedy ta ma external linkage) - wtedy tak naprawdę, wybierana jest podczas linkowania tylko definicja z pliku def.h, pomimo, że podczas kompilacji bar.cpp znana jest zupełnie inna definicja foo().

U mnie wtedy output to:

0

1

a nie:

10

11

jak powinno być wg tego co napisałeś… U mnie więc g++ (przy tych flagach co ustawiałeś) wybiera funkcję inline taką jaka jest w danym pliku…

Problem tego przykładu polega na tym że o ile funkcja inline posiada jakąś pętle bądź zmienną statyczną to już przestaje być inline.

Poza tym poczytajcie sobie jak g++ i mingw traktują funkcje inline - odbiega od standardu.

bob34 , no to u mnie (g++ 4.4.3 na Linuksie) w zależności od tego, w jakiej kolejności podam argumenty:

g++ main.cpp bar.cpp -o main

lub

g++ bar.cpp main.cpp -o main

dostaję odpowiednio:

10

11

oraz

-10

-11

Wydaje mi się, że taka rozbieżność może wynikać z tego, że taka sytuacja, jak już wyżej napisałem, jest zabroniona przez standard.

OK skoro [alex] powiedział ze g++ tutaj nie respektuje standardu to nie drążmy już tego tematu :slight_smile:

Podsumowując: funkcja inline musi być zdefiniowana za każdym razem kiedy chcemy jej użyć w danej jednostce translacyjnej i ta definicja musi być taka sama. Konflikt nie wystąpi (jak kompilator odrzuci prosbę o inline) pomimo external linkage.

Ostatnie zdanie jest bardzo logiczne ^.^ - ale to już zasługa twórcow. Nie było by tego tematu gdyby inline była po prostu static…

Moment, static to już inna bajka.

static jest widoczny tylko w jednej swojej jednostce translacyjnej. Czyli w każdym pliku może być inna funkcja o tej samej nazwie oraz tymi samymi parametrami.

bob34 , gdyby w powyższym przykładzie foo() miało internal linkage (niewykomentowane static), program wypisałby:

5

-5

co nie zawsze jest pożądane. Tym bardziej byłoby mylące, ponieważ domyślnie wszystkie funkcje w globalnej przestrzeni nazw są z external linkage.

I z tym mamy tu właśnie do czynienia…

OK, ja tylko mówię, że mnie by się to wydawało normalne wtedy…

Nie zupełnie. Bo inline to prawie jak static (musi być również zdefiniowana przed miejscem wywołania) dopóki z jakieś racji kompilator nie zdecyduje na rezygnacje z jej inline’ości wtedy to już normalna funkcja - nie static, z tym że nie daje ostrzeżeń o dublowaniu i berze pierwszą lepszą z nich (jeżeli jest ich kilka).

g++ - nie obsługuje inline.

No to takie external linkage obojętne na mnogość takiej samej funkcji ;p

“berze pierwszą lepszą z nich”

Aaaa, to dlatego muszą być takie same! - żeby nie było wątpliwości którą wybrać.

Kto powiedział że muszą być takie same? Wcale nie muszą. W każdym pliku możesz mieć inną funkcje inline o tej samej nazwie. O ile kompilator obsługuje te inline to będzie w każdym pliku inna funkcja wykonywana. Ale jak kompilator z jakiś przyczyn “anuluje” inline to traktuje to jako normalną funkcje. A linkier we wszystkich takich miejscach podstawi pierwszą lepszą co właściwie przydarzyło się flash4gordon’owi kiedy która funkcją się wykona zależało od kolejności linkowania.

Standard :lol:

No właśnie pierwszą lepszą… Lepiej więc robić tak jak mowi standard - takie same funkcje inline, i wtedy nie musimy sie zastanawiac która wygra w losowaniu, jezeli inline nie zostanie wzięte pod uwage…

Wygląda na to że w tej dyskusji absolutnie nic nie zrozumiałeś. Dopóki masz przyzwoite funkcje inline a kompilator ich obsługuje to wszystko gra.

Co do standardu - Nie masz szans dopilnować żeby pisząc jakiś program nie użyć jakieś nazwy dla funkcji inline która przypadkiem została użyta w jednej z dziesięciu bibliotek które dołączyłeś do programu.

Więc poczytaj ten standard uważniej.

[alex], ISO/IEC 14882:2003(E) 7.1.2.2

zatem g++ respektuje tu standard, ponieważ wcale nie musi respektować inline.

Dlatego warto używać static (internal linkage) lub własnych przestrzeni nazw. Przecież nie będziesz sprawdzał co kompilację, czy kompilator przypilnował inline czy nie.

… lub w inny sposób pilnować unikalności nazw funkcji inline, z tym że te sposoby co wymieniłeś są najlepsze.

Nie mam szans, ale taki błąd zostanie wychwytany przez kompilator - nie mogą istnieć chyba 2 funkcje o tej samej nazwie w tym samym pliku, prawda ? Więc problem praktycznie nie istnieje, bo nazwe szybko zmienimy na jakas inna, nie wchodzaca w konflikt…

Właśnie że nie, nie zostanie wychwycony bo zaznaczyłeś jako inline.