Szczegóły instancji szablonów kompilatorów GCC i MS

Czy ktoś mógłby podać porównanie lub konkretne szczegóły jak jest instancja szablonu obsługiwane w czasie kompilacji i / lub łącza w kompilatorach GCC i MS? Czy ten proces jest inny w kontekście bibliotek statycznych, bibliotek współdzielonych i plików wykonywalnych? Znalazłem Ten doc o tym, jak GCC sobie z tym radzi, ale nie jestem pewien, czy informacje nadal odnosi się do obecnego stanu rzeczy. Czy powinienem używać FLAG sugerują tam przy kompilowaniu moich bibliotek np. - FNO-implicit-templates ?

To co wiem (niekoniecznie poprawne) to:

  • szablony zostaną utworzone, gdy faktycznie są używane
  • szablony będą tworzone jako instancje w wyniku jawnych instancji
  • W przeciwieństwie do innych języków, w których istnieje wiele języków, w których istnieje wiele języków, w których istnieje wiele języków.]}
Author: wallyk, 2011-08-25

2 answers


Punkt instancji

Szablony zostaną utworzone, gdy są faktycznie używane

Nie do końca, ale z grubsza. Dokładny punkt instancji jest nieco subtelny i deleguję Cię do sekcji o nazwie Punkt instancji w pięknej książce Vandevoorde' a/Josuttisa.

Kompilatory nie muszą jednak poprawnie implementować Poi: błąd c++/ 41995: niepoprawny punkt instancji dla funkcji szablon


Częściowa instancja

Szablony zostaną utworzone, gdy są faktycznie używane

To jest częściowo poprawne. To prawda w przypadku szablonów funkcji, ale w przypadku szablonów klas tworzy się instancje tylko funkcji Członkowskich, które są używane. Poniższy jest dobrze uformowany kod:

#include <iostream>

template <typename> struct Foo {
    void let_me_stay() {
        this->is->valid->code. get->off->my->lawn;
    }

    void fun() { std::cout << "fun()" << std::endl; } 
};


int main () {
    Foo<void> foo;
    foo.fun();
}

let_me_stay() jest sprawdzana składniowo (a składnia jest poprawna), ale nie semantycznie (tzn. nie jest interpretowane).


Wyszukiwanie dwufazowe

Jednak dopiero kod zależny jest interpretowany później; wyraźnie, w Foo<>, this zależy od dokładnego ID szablonu, z którym Foo<> jest tworzona instancja, więc odłożyliśmy sprawdzanie błędów Foo<>::let_me_alone() do czasu tworzenia instancji.

Ale jeśli nie używamy czegoś, co zależy od konkretnej instancji, kod musi być dobry. Dlatego poniżej znajduje się nie dobrze uformowany:

$ cat non-dependent.cc
template <typename> struct Foo {
    void I_wont_compile() { Mine->is->valid->code. get->off->my->lawn; }
};
int main () {} // note: no single instantiation

Mine jest zupełnie nieznanym symbolem dla kompilatora, w przeciwieństwie do this, dla którego kompilator może określić jego zależność od instancji.

Kluczowym punktem jest tutaj to, że C++ używa modelu two-phase-lookup , gdzie sprawdza kod nie zależny w pierwszej fazie, a semantyczne sprawdzanie kodu zależnego odbywa się w fazie drugiej (i czas instancji) (jest to również często niezrozumiane lub nieznane pojęcie, wielu programistów C++ zakłada, że że szablony nie są w ogóle przetwarzane aż do momentu utworzenia, ale to tylko mit pochodzący z, ..., Microsoft C++).


Pełna instancja szablonów klas

Definicja Foo<>::let_me_stay() zadziałała, ponieważ sprawdzanie błędów zostało przełożone na później, jak dla wskaźnika this, który jest zależny. Chyba, że skorzystałbyś z

Jawne instancje

cat > foo.cc
#include <iostream>

template <typename> struct Foo {
    void let_me_stay() { this->is->valid->code. get->off->my->lawn; }
    void fun() { std::cout << "fun()" << std::endl; } 
};

template struct Foo<void>;
int main () {
    Foo<void> foo;
    foo.fun();
}

g++ foo.cc
error: error: ‘struct Foo<void>’ has no member named ‘is’


Definicje szablonów w różnych jednostkach tłumaczenie

Kiedy jawnie tworzysz instancję, jawnie tworzysz instancję. I sprawiają, że wszystkie symbole są widoczne dla linkera, co oznacza również, że definicja szablonu może znajdować się w różnych jednostkach tłumaczenia:

$ cat A.cc
template <typename> struct Foo {
    void fun();  // Note: no definition
};
int main () {
    Foo<void>().fun();
}

$ cat B.cc
#include <iostream>
template <typename> struct Foo {
    void fun();

};
template <typename T>
void Foo<T>::fun() { 
    std::cout << "fun!" << std::endl;
}  // Note: definition with extern linkage

template struct Foo<void>; // explicit instantiation upon void

$ g++ A.cc B.cc
$ ./a.out
fun!

Należy jednak jawnie utworzyć instancję dla wszystkich argumentów szablonu, w przeciwnym razie

$ cat A.cc
template <typename> struct Foo {
    void fun();  // Note: no definition
};
int main () {
    Foo<float>().fun();
}
$ g++ A.cc B.cc
undefined reference to `Foo<float>::fun()'

Mała uwaga na temat wyszukiwania dwufazowego: to, czy kompilator faktycznie implementuje wyszukiwanie dwufazowe, nie jest podyktowane przez standard. Na być zgodne, jednak powinno działać tak, jakby tak było (tak jak dodawanie lub mnożenie niekoniecznie musi być wykonywane za pomocą instrukcji dodawania lub mnożenia procesora.

 54
Author: Sebastian Mach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-13 22:01:28

Edit : okazuje się, że to co napisałem poniżej jest sprzeczne ze standardem C++. Jest to prawda dla Visual C++, ale fałsz dla kompilatorów, które używają "dwufazowego wyszukiwania nazw".

Z tego co wiem, to co mówisz jest poprawne. Szablony będą tworzyły instancje, gdy są faktycznie używane (także wtedy, gdy są zadeklarowane jako element innego typu, ale nie wtedy, gdy są wymienione w deklaracji funkcji (która nie ma ciała)) lub w wyniku jawnych instancji.

Problem z szablonami to że jeśli użyjesz tego samego szablonu (np. wektora) w kilku różnych jednostkach kompilacji (.pliki cpp), kompilator powtarza pracę tworzenia instancji szablonu w każdym .plik cpp, co spowalnia kompilację. IIRC, GCC ma jakieś (niestandardowe?) mechanizm, który można użyć, aby tego uniknąć (ale nie używam GCC). Ale Visual C++ zawsze powtarza tę pracę, chyba że użyjesz jawnej instancji szablonu w wstępnie skompilowanym nagłówku (ale nawet to spowolni kompilację, ponieważ większy plik PCH zajmuje dłużej ładować.) Następnie łącznik eliminuje duplikaty. Uwaga : komentarz poniżej połączony ze stroną , która mówi nam, że nie wszystkie Kompilatory działają w ten sposób. Niektóre Kompilatory odkładają instancję funkcji do czasu połączenia, co powinno być bardziej efektywne.

Szablon nie jest w pełni utworzony, gdy jest używany po raz pierwszy. W szczególności funkcje w szablonie nie są inicjowane, dopóki nie zostaną faktycznie wywołane. Możesz to łatwo zweryfikować, dodając nonsens funkcja do szablonu, którego aktywnie używasz:

void Test() { fdsh "s.w" = 6; wtf? }

Nie pojawi się błąd, chyba że utworzysz jawnie szablon lub spróbujesz wywołać funkcję.

Spodziewam się, że biblioteki statyczne (i pliki obiektowe) będą przechowywać kod obiektowy wszystkich szablonów, które zostały utworzone. Ale jeśli twój program ma pewną statyczną bibliotekę jako zależność, nie możesz w rzeczywistości wywołać funkcji szablonu, które były już w niej utworzone, przynajmniej nie w VC++, co zawsze wymaga źródła kod (z ciałami funkcyjnymi) klasy szablonu w celu wywołania w niej funkcji.

Nie wydaje mi się, aby możliwe było wywołanie funkcji szablonu w bibliotece współdzielonej (jeśli nie masz kodu źródłowego funkcji szablonu, którą chcesz wywołać).

 -2
Author: Qwertie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-06 19:13:47