tags:

views:

453

answers:

7

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.

+1  A: 

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

Jim Buck
A: 

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.

Frank Krueger
A: 

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

grigy
+4  A: 

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.

Alastair
+3  A: 

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).

Andreas Magnusson
Not quite. The compiler is required to generate a separate type for each value of S. If the compiler deems it safe, it can of course optimize by merging the multiple instantiations back together or inlining. But they are required by the standard to be treated as independent types.
jalf
I'm sorry but I reread my post and couldn't find the part that contradicts what you said. You're talking about compiler internals, I'm talking about compiler output. Since you misinterpret me, others may as well, so could you please tell me what would make my post less ambiguous in this aspect?
Andreas Magnusson
"It's only required to generate correct code for its inputs" could be changed into "It's only required to generate correct code for its inputs, which means, at the very least, consider the different instantiations as different types"... ^_^
paercebal
To those who down-vote this answer, it would be interesting with some constructive criticism as well not just a silent down vote.
Andreas Magnusson
It's a bit frustrating when you're trying to help and people seem to think my answer is not helpful but won't explain why it isn't. I can live with the down-votes but I'd at least like the chance of making my answer as good as possible. Please.
Andreas Magnusson
+1  A: 

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).

MSalters
+2  A: 

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.

Johannes Schaub - litb