Точка воплощения
шаблоны будут созданы при фактическом использовании
Не совсем, но примерно. Точная точка инстанцирования немного тонка, и я делегирую вас в раздел под названием Точка инстанцирования в прекрасной книге Вандевурда / Хосуттиса.
Однако компиляторы не обязательно правильно реализуют POI: Ошибка c ++ / 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 ++ использует модель двухфазный поиск , где он выполняет проверку для независимого кода на первом этапе, а семантическая проверка для зависимого кода выполняется в вторая фаза (и время создания экземпляра) (это также часто неверно понимаемая или неизвестная концепция, многие программисты на C ++ предполагают, что шаблоны не анализируются вообще до момента создания экземпляра, но это всего лишь миф, исходящий из ..., Microsoft C ++).
Полная реализация шаблонов классов
Определение 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()'
Небольшое замечание о двухфазном поиске. Реализует ли компилятор двухфазный поиск стандартом, не диктуется. Однако, чтобы соответствовать, он должен работать так, как если бы он работал (точно так же, как сложение или умножение не обязательно должны выполняться с использованием инструкций ЦП сложения или умножения.