Как создается экземпляр шаблона?

Это упражнение из С++ Primer 5th Edition:

Упражнение 16.27: для каждого помеченного заявления объясните, что, если таковое имеется, экземпляры происходят. Если шаблон создается, объясните, почему; если не объясните, почему нет. P.677

template <typename T> class Stack { };

void f1(Stack<char>);                   // (a)

class Exercise {
    Stack<double> &rsd;                 // (b)
    Stack<int>    si;                   // (c)
};

int main() {
    Stack<char> *sc;                    // (d)
    f1(*sc);                            // (e)
    int iObj = sizeof(Stack< string >); // (f)
}

Ниже я попытался:

(a) Stack<char> создается, но его экземпляр не создается.

(b) Stack<double> создается, но ни один из его экземпляров не создается.

(c) Stack<int> и его конструктор по умолчанию создается.

(d) (e) совершенно не знаю...

(f) Stack< string > создается, но ни один из его экземпляров не создается.

Я прав? Может ли кто-нибудь сказать мне, как этот код создается?

Ответы

Ответ 1

В вашем конкретном случае декларация не означает создание экземпляра

#include <iostream>
using namespace std;


template <typename T> class Stack {
  typedef typename T::ThisDoesntExist StaticAssert; // T::NotExisting doesn't exist at all!
};


void f1(Stack<char>); // No instantiation, compiles

class Exercise {
  Stack<double> &rsd; // No instantiation, compiles (references don't need instantiation, are similar to pointers in this)

  Stack<int>    si; // Instantiation! Doesn't compile!!
};


int main(){

  Stack<char> *sc; // No Instantiation, this compiles successfully since a pointer doesn't need instantiation

  f1(*sc); // Instantiation of Stack<char>! Doesn't compile!!

  int iObj = sizeof(Stack< std::string >); // Instantiation of Stack<std::string>, doesn't compile!!

}

обратите внимание на материал указателя/ссылки: им не требуется инстанцирование, так как данные фактически не выделены (указатель содержит всего несколько байтов, чтобы содержать адрес, не нужно хранить все данные. Посмотрите на идиома pimpl).

Только когда материал выделяется, тогда шаблон должен быть полностью разрешен (и это происходит во время компиляции, поэтому для них обычно требуются как декларация, так и определение.. там еще нет фазы связывания)

Ответ 2

Что касается e и d, я приведу стандарт 14.7.1

Если спецификация шаблона функции явно не указана экземпляр или явно специализированный, шаблон функции специализация неявно создается, когда специализация ссылается в контексте, который требует определения функции. Если вызов не является явно выраженной специализацией или функция-член явно специализированного шаблона класса, аргумент по умолчанию для шаблона функции или функции-члена шаблон класса неявно создается при вызове функции в контексте, который требует значения аргумента по умолчанию.

Пример также из стандартного

template<class T> struct Z {
void f();
void g();
};

void h() 
{
Z<int> a;     // instantiation of class Z<int> required
Z<char>* p;   // instantiation of class Z<char> not required
Z<double>* q; // instantiation of class Z<double> not required
a.f();        // instantiation of Z<int>::f() required
p->g();       // instantiation of class Z<char> required, and instantiation of Z<char>::g() required
}

Это означает, что никакой инстанцирования не происходит в d. Хотя он будет создан в e, если эта функция действительно необходима для вызова функции из этого типа (может быть конструктором копирования или любой другой функцией, называемой внутри функции).