21

It's an exercise from C++ Primer 5th Edition:

Exercise 16.27: For each labeled statement explain what, if any, instantiations happen. If a template is instantiated, explain why; if not, explain why not. 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)
}

Below is what I tried:

(a) Stack<char> is instantiated , but no member of it is instantiated.

(b) Stack<double> is instantiated , but no member of it is instantiated.

(c) Stack<int> and its default constructor are instantiated.

(d) (e) totally no idea...

(f) Stack< string > is instantiated , but no member of it is instantiated.

Am I right? Can anyone tell me how this code is instantiated?

Yue Wang
  • 1,710
  • 3
  • 18
  • 43
  • 5
    When in doubt, you can add dependent static assertions (or something similar to force a compile-time error), for example assert that `T` is `void`. The failed assertion in a member function would then tell you that the member function was instantiated. When you know what happens, explaining why it happens becomes much easier. The failed assertion outside of a member function would similarly tell you that the class itself was instantiated. –  Feb 06 '14 at 09:45
  • @hvd Thx man. Since I never used assertion before. I'm trying to understand this approach. In this case, since all copy and control members are synthesized version, should I write custom version like default constructor and put assertion into it? If it doesn't compile, it means this default constructor is instantiated.Am I right? – Yue Wang Feb 06 '14 at 10:34
  • 1
    Yes, that's what I meant. You first check (using David Kernin's answer) in which cases the class itself gets instantiated. You can then change the class (copying the assertion style from the answer) to, for example, `template class Stack { public: Stack() { typedef typename T::ThisDoesntExist StaticAssert; } };` to detect where the default constructor gets used. –  Feb 06 '14 at 10:55

2 Answers2

14

In your specific case a declaration doesn't mean an instantiation

#include <iostream>
using namespace std;


template <typename T> class Stack {
  typedef typename T::ThisDoesntExist StaticAssert; // T::ThisDoesntExist 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!!
 
}

notice the pointer/reference stuff: they don't require instantiation since no data is actually allocated (a pointer is just a few bytes to contain the address, has no need to have all the data stored.. take a look at the pimpl idiom ).

Only when stuff is allocated then the template has to be completely resolved (and that happens at compile-time, that's why they usually need both declaration and definition.. there's no linking phase yet)

rawrex
  • 4,044
  • 2
  • 8
  • 24
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • I have never used assertion before.While trying to compile the statement `Stack si;` from your code, it was complained that `'int' is not a class, struct, or union type`.How to understand it? – Yue Wang Feb 06 '14 at 10:27
  • 1
    If you remove all the "doesn't compile" lines and comment those out, it should compile: http://ideone.com/Amopp5 . – Marco A. Feb 06 '14 at 10:31
  • 1
    Also: that specific line requires to know **how big** the Exercise object will be. You can't know that in advance if you don't instantiate the si object while for a pointer it doesn't really matter (on x64 systems a pointer is 8-byte wide). – Marco A. Feb 06 '14 at 10:40
  • So ... no matter what object a pointer is pointing to, the size of the pointer is fixed, always 8-byte. The complier will always use 8-byte space to store a pointer, even before instantiation. But for an object the compiler won't know the size of it until instantiation. Do you mean this? – Yue Wang Feb 06 '14 at 10:53
  • 1
    On x64 systems, 8 bytes. On 32-bit x86 systems 4 bytes and so on depending on the architecture. The type of the pointer matters but it's not relevant to know how much space should be allocated in advance. – Marco A. Feb 06 '14 at 10:57
3

Regarding e and d I will quote the standard 14.7.1

Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.

Example also from the standard

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
}

This means that no instantiation happens in d. While it will be instantiated in e if that function actually needed to call a function from that type ( could be a copy constructor or any other function called inside the function).

TMOTTM
  • 3,286
  • 6
  • 32
  • 63
concept3d
  • 2,248
  • 12
  • 21