Помимо получения указателя на ваш собственный объект для передачи (или возврата) другим функциям и определения того, что идентификатор является членом, даже если он скрыт локальной переменной, в программировании шаблонов есть действительно надуманное использование этого.,Это использование преобразовывает независимое имя в зависимое имя.Шаблоны проверяются за два прохода, сначала до фактической замены типа, а затем снова после замены типа.
Если вы объявляете класс шаблона, производный от одного из его параметров типа, вам необходимо квалифицировать доступ к членам базового класса.так что компилятор обходит проверку на первом проходе и оставляет проверку для второго прохода:
template <typename T>
struct test : T {
void f() {
// print(); // 1st pass Error, print is undefined
this->print(); // 1st pass Ok, print is dependent on T
}
};
struct printer {
void print() { std::cout << "print"; }
};
struct painter {
void paint() { std::cout << "paint"; }
};
int main() {
test<printer> t; // Instantiation, 2nd pass verifies that test<printer>::print is callable
t.f();
//test<painter> ouch; // 2nd pass error, test<painter>::print does not exist
}
Важным битом является то, что, поскольку test
наследует от T
, все ссылки на this
в зависимости от аргумента шаблона T
и, как таковой, компилятор предполагает, что он верен, и оставляет фактическую проверку на втором этапе.Существуют и другие решения, такие как определение типа, реализующего метод, например:
template <typename T>
struct test2 : T {
void f() {
T::print(); // 1st pass Ok, print is dependent on T
}
};
Но это может привести к нежелательному побочному эффекту: компилятор будет статически отправлять вызов printer::print
независимо от того,* является ли printer
виртуальным методом или нет.Таким образом, при объявлении printer::print
виртуальным, если класс наследуется от test<print>
и реализует print
, то будет вызываться этот последний переопределитель, тогда как если тот же класс получен из test2<print>
, код будет вызывать printer::print
.
// assumes printer::print is virtual
struct most_derived1 : test<printer> {
void print() { std::cout << "most derived"; }
};
struct most_derived2 : test2<printer> {
void print() { std::cout << "most derived"; }
};
int main() {
most_derived1 d1;
d1.f(); // "most derived"
most_derived2 d2;
d2.f(); // "print"
}