chappar
chappar

Reputation: 7535

c++ template instantiation

I have a template class like below.

template<int S> class A
{
private:
  char string[S];

public:
  A()
  {
    for(int i =0; i<S; i++)
    {
      .
      .
    }
  }

  int MaxLength()
  {
    return S;
  }
};

If i instantiate the above class with different values of S, will the compiler create different instances of A() and MaxLenth() function? Or will it create one instance and pass the S as some sort of argument?

How will it behave if i move the definition of A and Maxlength to a different cpp file.

Upvotes: 0

Views: 1609

Answers (7)

Andreas Magnusson
Andreas Magnusson

Reputation: 7434

Actually this is fully up to the compiler. It's only required to generate correct code for its inputs. In order to do so it must follow the C++ standard as that explains what is correct. In this case it says that the compiler must at one step in the process instantiate templates with different arguments as different types, these types may later be represented by the same code, or not, it's fully up to the compiler.

It's most probable that the compiler would inline at least MaxLength() but possibly also your ctor. Otherwise it may very well generate a single instance of your ctor and pass/have it retrieve S from elsewhere. The only way to know for sure is to examine the output of the compiler.

So in order to know for sure I decided to list what VS2005 does in a release build. The file I have compiled looks like this:

template <int S>
class A
{
  char s_[S];
public:
  A()
  {
    for(int i = 0; i < S; ++i)
    {
      s_[i] = 'A';
    }
  }
  int MaxLength() const
  {
    return S;
  }
};

extern void useA(A<5> &a, int n); // to fool the optimizer
extern void useA(A<25> &a, int n);

void test()
{
  A<5> a5;
  useA(a5, a5.MaxLength());
  A<25> a25;
  useA(a25, a25.MaxLength());
}

The assembler output is the following:

?test@@YAXXZ PROC                   ; test, COMDAT

[snip]

; 25   :    A<5> a5;

mov eax, 1094795585             ; 41414141H
mov DWORD PTR _a5$[esp+40], eax
mov BYTE PTR _a5$[esp+44], al

; 26   :    useA(a5, a5.MaxLength());

lea eax, DWORD PTR _a5$[esp+40]
push    5
push    eax
call    ?useA@@YAXAAV?$A@$04@@H@Z       ; useA

As you can see both the ctor and the call to MaxLength() are inlined. And as you may now guess it does the same with the A<25> type:

; 28   :    A<25> a25;

mov eax, 1094795585             ; 41414141H

; 29   :    useA(a25, a25.MaxLength());

lea ecx, DWORD PTR _a25$[esp+48]
push    25                  ; 00000019H
push    ecx
mov DWORD PTR _a25$[esp+56], eax
mov DWORD PTR _a25$[esp+60], eax
mov DWORD PTR _a25$[esp+64], eax
mov DWORD PTR _a25$[esp+68], eax
mov DWORD PTR _a25$[esp+72], eax
mov DWORD PTR _a25$[esp+76], eax
mov BYTE PTR _a25$[esp+80], al
call    ?useA@@YAXAAV?$A@$0BJ@@@H@Z     ; useA

It's very interesting to see the clever ways the compiler optimizes the for-loop. For all those premature optimizers out there using memset(), I would say fool on you.

How will it behave if i move the definition of A and Maxlength to a different cpp file.

It will probably not compile (unless you only use A in that cpp-file).

Upvotes: 5

Jim Buck
Jim Buck

Reputation: 20724

Wherever S is used, a separate version of that function will get compiled into your code for each different S you instantiate.

Upvotes: 1

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507313

If i instantiate the above class with different values of S, will the compiler create different instances of A() and MaxLenth() function? Or will it create one instance and pass the S as some sort of argument?

The compiler will instantiate a different copy of the class template for each different value of the parameter. Regarding the member functions, it will instantiate a different copy of each, for each different value of S. But unlike member functions of non-template classes, they will only be generated if they are actually used.

How will it behave if i move the definition of A and Maxlength to a different cpp file.

You mean if you put the definition of A into a header file, but define the member function MaxLength in a cpp file? Well, if users of your class template want to call MaxLength, the compiler wants to see its code, since it wants to instantiate a copy of it with the actual value of S. If it doesn't have the code available, it assumes the code is provided otherwise, and doesn't generate any code:

A.hpp

template<int S> class A {
public:
    A() { /* .... */ }
    int MaxLength(); /* not defined here in the header file */
};

A.cpp

template<int S> int
A<S>::MaxLength() { /* ... */ }

If you now only include include A.hpp for the code using the class template A, then the compiler won't see the code for MaxLength and won't generate any instantiation. You have two options:

  • Include the file A.cpp too, so the compiler sees the code for it, or
  • Provide an explicit instantiation for values of S you know you will use. For those values, you won't need to provide the code of MaxLength

For the second option, this is done by putting a line like the following inside A.cpp:

template class A<25>;

The compiler will be able to survive without seeing code for the member functions of A<25> now, since you explicitly instantiated a copy of your template for S=25. If you don't do any of the two options above, the linker will refuse to create a final executable, since there is still code missing that is needed.

Upvotes: 2

MSalters
MSalters

Reputation: 180225

A<S>::MaxLength() is so trivial it will be fully inlined. Hence, there will be 0 copies. A<S>::A() looks more complex, so it likely will cause multiple copies to be generated. The compiler may of course decide not to, as long as the code works as intended.

You might want to see if you can move the loop to A_base::A_base(int S).

Upvotes: 1

Alastair
Alastair

Reputation: 4523

The template will be instantiated for each different values of S.

If you move the method implementations to a different file, you'll need to #include that file. (Boost for instance uses the .ipp convention for such source files that need to be #included).

If you want to minimise the amount of code that is generated with the template instantiation (and hence needs to be made available in the .ipp file) you should try to factor it out by removing the dependency on S. So for example you could derive from a (private) base class which provides member functions with S as a parameter.

Upvotes: 5

grigy
grigy

Reputation: 6836

Compiler will create different instances of the class if you instantiate it for different types or parameters.

Upvotes: 0

Frank Krueger
Frank Krueger

Reputation: 71053

It will create two different versions of A() and MaxLength() that will return compile-time constants. The simple return S; will be compiled efficiently and even inlined where possible.

Upvotes: 0

Related Questions