Creating a C ++ Template

I have a templated class as shown 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 S values, will the compiler create different instances of the A () and MaxLenth () functions? Or will it create one instance and pass S as some argument?

How it will look if I moved the definition of A and Maxlength to another cpp file.

0


source to share


7 replies


The template will be created for different S.

If you move method implementations to a different file, you will need to # include that file. (For example, Boost uses a convention .ipp

for such source files to be included #).



If you want to minimize the amount of code generated by the template instance (and therefore should be available in the file .ipp

), you should try to exclude it by removing the dependency on S. So, for example, you can get from a (private) base a class that provides member functions with S as a parameter.

+5


source


This is actually completely compiler-dependent. You only need to generate the correct code for your inputs. To do this, it must follow the C ++ standard, as that explains what is correct. In this case, he says that the compiler should, at one stage in the process, create templates with different arguments as different types, these types can later be represented by the same code, or not, entirely up to the compiler.

Most likely the compiler would have set at least MaxLength (), but perhaps your ctor as well. Otherwise it might very well create one instance of your ctor and pass / receive S from elsewhere. The only way to know for sure is to check the compiler output.

So, in order to know for sure, I decided to list what VS2005 does in the 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 as follows:

?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 MaxLength () call are inline. And, as you can now guess, it does the same with type A <25>:

; 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 using memset (), I would say you are a fool.

How it will look if I moved the definition of A and Maxlength to another cpp file.

It probably won't compile (unless you only use A in that cpp file).

+5


source


If I instantiate said class using different S values, will the compiler create different instances of A () and MaxLenth ()? Or does it create one instance and pass S as some kind of argument?

The compiler will instantiate another copy of the class template for every different parameter value. As far as member functions are concerned, it will instantiate every copy for every other value S

. But unlike member functions of classes other than templates, they will only be generated if they are actually used.

How it will look if I moved the definition of A and Maxlength to another cpp file.

Do you mean if you put the definition A

in a header file but define a member function MaxLength

in the 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 it with the actual value S

. If it has no code, it assumes that the code is specified differently and does not 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 include include A.hpp for code using class A template, then the compiler will not see the code for MaxLength

and will not generate any instances. You have two options:

  • Include the A.cpp file too, so the compiler sees the code for it, or
  • Provide explicit creation of values S

    that you know you will use. For these values, you don't need to provide any codeMaxLength

For the second option, this is done by placing a line like below: A.cpp:

template class A<25>;

      

The compiler will be able to survive without going through the code for the member functions now A<25>

, since you've explicitly instantiated your template for S=25

. If you don't do either of the two options above, the linker will refuse to create the final executable file as the code you need is still missing.

+2


source


Wherever S is used, a separate version of this function will be compiled into your code for each different instance you create.

+1


source


A<S>::MaxLength()

so trivial that it will be fully nested. Hence there will be 0 instances. A<S>::A()

looks more complicated, so it will probably cause multiple copies to be created. The compiler may of course disagree if the code works as intended.

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

+1


source


It will create two different versions A()

and MaxLength()

, which will return compile time constants. Simple return S;

will compile efficiently and even inline where possible.

0


source


The compiler will create different instances of the class if you instantiate for different types or parameters.

0


source







All Articles