0

I am learning C++ and it hasn't been an enjoyable experience (compared to Java or VBA at least). I have the following code:

//This is in a number.h file
  #pragma once
  template <class T>
  class number{
        public: 
               T value1, value2, result; 
        public: 
               T add();
               number(T value1_in, T value2_in);
  };

 //This is in a number.cpp file
   template <class T>
   number<T>::number(T value1_in, T value2_in){
              value1 = value1_in;
              value2 = value2_in;
   }

   template <class T>
   T number<T>::add(){
   result = value1 + value2; 
   return result; 
   }

 //This is in main.cpp
   #include "number.h"
   #include <iostream>
   using namespace std;

   int main(){
       int a = 2, b =3;

       number<int> n1(a,b);
       cout << n1.add();
       system("pause");
       return EXIT_SUCCESS;
  }

Which of course gives me an error. Even though I am pretty sure it should work. More specifically I get a linker error. After 3 hours of looking at this I decided to include number.cpp in main.cpp and that magically made it work. What the hell is going on? I thought I only need to include the header file (I wrote a matrix class with a bunch of linear solvers for different algorithms before this and only included header files in the whole project). Is this C++ specific or compiler specific? (I am using Dev-C++ 4.9.9.2 which has Mingw I guess)

Kara
  • 6,115
  • 16
  • 50
  • 57
YsK
  • 13
  • 3
  • Duplicate question. There are two solutions. See [this answer to another question](http://stackoverflow.com/a/8752879/146041) – Aaron McDaid Jan 15 '12 at 02:54
  • possible duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Cody Gray - on strike Jan 15 '12 at 08:31

3 Answers3

3

Templated classes or functions always need to stay in the header file.

The reason is that whenever you instantiate a template, the preprocessor (inside compiler) generates new code for exactly that kind of instantiation (e.g. number<double>). That's why the classes number<double> and number<int> will not share any relationship: They will be two completely different classes although both were generated from the same template.

For the compiler to be able to generate this code, it must know the whole template definition, not only its declaration. That's why a template needs to stay in the header in full.

Including the cpp file in your main.cpp did the trick, as it effectively became a header.

ypnos
  • 50,202
  • 14
  • 95
  • 141
  • Isn't this template already in the header file? Or the fact that it is declared both in number.h and number.cpp somehow confuses the compiler? What options do I have for fixing this besides including number.cpp in main? – YsK Jan 15 '12 at 02:26
  • You have to include (not #include) the contents of number.cpp in number.h, and you #include only the header. – buc Jan 15 '12 at 02:28
  • Ok. I c. Is that always the case? I was told to separate the .h files from the .cpp files where write the implementation (especially for the long algorithms). I guess that's not a good thing to do? – YsK Jan 15 '12 at 02:33
  • As I described, templates have special needs and are therefore an excemption to the rule you mention. We have to live with it. – ypnos Jan 15 '12 at 02:36
  • -1. It is not entirely necessary to put the implementation into the header. There is another valid solution, which I've used on a number of occasions. See http://stackoverflow.com/a/8752879/146041 Depending on context, the explicit instantiation within a cpp file makes a lot of sense. – Aaron McDaid Jan 15 '12 at 02:51
  • ... it certainly is the conventional answer to put the definition in the header, and it's what I would usually do myself. But it's not a hard-and-fast rule. – Aaron McDaid Jan 15 '12 at 02:53
  • I see your point but don't think a downvote is justified. Your solution works only for a very specific application of templates, as the usual idea of templating is to give _others_ the freedom of using your code with various types, not only the ones you thought of. YsK is a beginner in the language and it is a far stretch to assume he will want to use templates only in the way you use them, therefore rendering my answer "not helpful". I will give you a +1 anyways at your answer adds more substance to the topic. – ypnos Jan 15 '12 at 15:24
  • Most of the answers here, and every other time I see this question on SO, state categorically that the implementation *must* be in the header file. It is this over-certainty that I object to, as it is incorrect. Otherwise, there are plenty of good answers, including this answer. – Aaron McDaid Jan 15 '12 at 16:05
3

You do not have to put the entire definition into the header file; don't listen to what others tell you :-)

That is the conventional solution, and you and I will do it quite often, but it's not the only solution.

The other option is to simply place this line at the end of your number.cpp file, in order to force that particular template class to be instantiated and fully compiled there.

template class number<int>;

In short, there are two valid solutions. You can use this line, or you can copy the definition into the header file. Depending on context, one approach might be better than the other, but both are valid. See this answer of mine for a more comprehensive discussion of both approaches:

https://stackoverflow.com/a/8752879/146041

Community
  • 1
  • 1
Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • 1
    Though this pretty much nerfs the genericity of templates. – Seth Carnegie Jan 15 '12 at 02:55
  • 2
    The disadvantage of your solution is that it restricts the types that the templated class can be used with. I understand that sometimes it may be desired, but it's not the common case. – buc Jan 15 '12 at 02:57
  • 1
    Of course, it's unusual to write a template and then need it to be instantiated for only one type. Kind of defeats the whole purpose in writing a class template. But yes, you can do this. – Cody Gray - on strike Jan 15 '12 at 08:33
  • @buc, that's often an advantage, not a disadvantage. For example, I wrote a class once that I was sure was correct for `int64_t` and for `string`. I had no plans to use it with other types. If I accidentally used with with `int32_t` or `char *` then I would be glad of the compilation error as it would force me to ask myself "have I tested this template for the new type? Did I plan for this eventuality?" In a private project, it is safer to maintain an explicit list of the template classes. – Aaron McDaid Jan 15 '12 at 12:14
  • +1 for the original answer, but what you write in your comment is quite bad. The clean and right solution to the problem of your class only working with a specific group of types is to use traits! – ypnos Jan 15 '12 at 15:29
  • Ah. Tell me more, @ypnos. I can't yet see how traits changes this. My particular example was a representation of a network, where the node names are either `string` or `int64_t`. Most of the `Network` and `Network` were identical to each other - hence I used a template. Some things did need to be specialized, I can't remember what I did but I probably did use small traits somewhere. So anyway, `Network` is just a class template, with two explicit instantiations. ( .. to be continued .. ) – Aaron McDaid Jan 15 '12 at 16:14
  • .... and I think it's a feature, not a bug, that the instantiations must be explicit. For example, the code would probably compile cleanly with `Network` but it behave badly as the client code might free the string after passing the pointer in. I want my class to take 'ownership' and hence I like being explicit about what types I know have been tested and are well-understood. – Aaron McDaid Jan 15 '12 at 16:16
  • .. in short, given that it was desirable to limit the types to those explicitly supported, I can't think of any other way to do this. But I'm curious and always learning on StackOverflow. – Aaron McDaid Jan 15 '12 at 16:18
  • Well, the idea is that you can indeed test for the type, or characteristics of the type, in your template code. For example you can explicitely test "is this a primitive type?", "is this a pointer?" and so on. A good starting point for reading is the Boost TypeTraits library which implements all this fancy stuff like is_pointer: http://www.boost.org/doc/libs/1_48_0/libs/type_traits/doc/html/index.html If you want to reject pointers, you can do so e.g. with an assert. So you are not overly restrictive on the choice of type. – ypnos Jan 16 '12 at 11:48
  • I like especially this example, it both shows how to use type traits and also gives a hint on what is possible (look at is_same): http://www.boost.org/doc/libs/1_48_0/libs/type_traits/doc/html/boost_typetraits/examples/iter.html – ypnos Jan 16 '12 at 11:56
2

In C++, templates are just as their name suggests: templates for a class, function etc. As the concrete type of the template parameter is not known in advance, and the compiled object code depends on the actual parameter, they are not getting compiled (as normal classes and functions) until you use them in any other source file and the compiler gets to know what type should it substitute into the template parameter.

That's why all functions must be defined and implemented in the header file. See the last section of this documentation or the answers for this similar question for further explanation.

Community
  • 1
  • 1
buc
  • 6,268
  • 1
  • 34
  • 51
  • It is *not* necessary to put the implementation into the header. There is another valid solution, which I've used on a number of occasions. See http://stackoverflow.com/a/8752879/146041 – Aaron McDaid Jan 15 '12 at 02:49