Ответ 1
Точка инстанцирования
шаблоны будут созданы при фактическом использовании
Не совсем, но грубо. Точная точка инстанцирования немного тонкая, и я передаю вас в раздел под названием Точка создания в замечательной книге Вандевоорда/Йосуттиса.
Однако компиляторы не обязательно правильно реализуют POI: Ошибка С++/41995: Неправильная точка создания экземпляра для шаблона функции
Частичная инстанция
шаблоны будут созданы при фактическом использовании
Это частично правильно. Это верно для шаблонов функций, но для шаблонов классов создаются экземпляры только тех функций-членов. Ниже приведен корректный код:
#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()
проверяется синтаксически (и синтаксис там правильный), но не семантически (т.е. он не интерпретируется).
Двухфазный поиск
Однако только зависимый код интерпретируется позже; ясно, что внутри Foo<>
, this
зависит от точного идентификатора шаблона, с которым создается Foo<>
, поэтому мы отложили проверку ошибок Foo<>::let_me_alone()
до момента создания экземпляра.
Но если мы не будем использовать что-то, что зависит от конкретного экземпляра, код должен быть хорошим. Следовательно, не:
$ 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
- полностью неизвестный символ компилятору, в отличие от this
, для которого компилятор мог определить его зависимость от экземпляра.
Ключевым моментом здесь является то, что С++ использует модель двухфазный поиск, где он проверяет независимый код в первая фаза и семантическая проверка зависимого кода выполняются во второй фазе (и времени создания экземпляра) (это также часто неправильно понимаемая или неизвестная концепция, многие программисты на C++ предполагают, что шаблоны не обрабатываются вообще до создания экземпляра, но это только миф, приходящий из,..., Microsoft С++).
Полная реализация шаблонов классов
Определение Foo<>::let_me_stay()
работало, потому что проверка ошибок была отложена до более поздней версии, как и указатель this
, который зависит. За исключением случаев, когда вы использовали
явные экземпляры
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’
Определения шаблонов в разных единицах перевода
Когда вы явно создаете экземпляр, вы создаете экземпляр явно. И сделайте все символы видимыми для компоновщика, что также означает, что определение шаблона может находиться в разных единицах перевода:
$ 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!
Однако вы должны явно создать экземпляр для всех аргументов шаблона, иначе
$ 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()'
Небольшое примечание о двухфазном поиске: действительно ли компилятор реализует двухфазный поиск, не продиктован стандартом. Однако, чтобы быть совместимым, он должен работать так, как если бы он выполнялся (так же как добавление или умножение необязательно должно выполняться с помощью инструкций ЦП или добавления или умножения.