3

Part of the header file dlist.h is defined as:

#ifndef __DLIST_H__
#define __DLIST_H__
#include <iostream>

class emptyList {};

template <typename T>
class Dlist {
 public:
    bool isEmpty() const;

 private:
    struct node {
    node   *next;
    node   *prev;
    T      *o;
    };

    node   *first; // The pointer to the first node (NULL if none)
    node   *last;  // The pointer to the last node (NULL if none)
};

#include "dlist.cpp"
#endif

When I create a dlist.cpp file like this:

#include "dlist.h"

template <typename T>
bool Dlist<T>::isEmpty() const
{
    return !first and !last;
}

I get the error message at line 4: redefinition of 'bool Dlist::isEmpty() const'

If I remove the #include "dlist.h" I get the error at line 4: expected initializer before '<' token

Any help here? Is there something I'm doing wrong that's not allowing me to just define my functions from the dlist.h file? Thank you.

Bob John
  • 3,688
  • 14
  • 43
  • 57

5 Answers5

7

You have to put the implementation of the class template's member functions in the header file or in a file included by the header. The compiler needs access to this code in order to instantiate templates for any given type T.

In your case, the problem seems to be that you are including the header in the .cpp and vice versa. If you really want to keep declaration and implementation in separate files, I suggest changing the implementation's suffix from .cpp to something else, e.g. .icpp. Some build systems might try to compile an object file out of anything with a .cpp suffix, and this would also result in error.

  1. Remove #include "dlist.h" from dlist.cpp.
  2. (optional) rename dlist.cpp to something like dlist.icpp. Why? Because many build systems automatically compile any file ending in .cpp into an object file. And many programmers assume that a .cpp file will be compiled into an object file.
  3. (only if step 2 taken) include the re-named dlist.icpp in dlist.h, as is currently done for dlis.cpp.
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • I'm not allowed to change the header file, so what can I do in this instance? – Bob John Dec 01 '12 at 10:50
  • in the latter case, it is considered good practise to name the file with the .tpp extension. – lucasg Dec 01 '12 at 10:51
  • 1
    @BobJohn remove `#include "dlist.h"` from your `.cpp` file. Perhaps change the suffix of the `.cpp`, and include it (as you do now) in your `.h`. – juanchopanza Dec 01 '12 at 10:52
  • My file has to be called dlist.cpp. – Bob John Dec 01 '12 at 10:54
  • @BobJohn OK, fine. Then just make sure your build environment doesn't try to compile it. – juanchopanza Dec 01 '12 at 10:55
  • 1
    `I'm not allowed to change the header file` Someone gave you a header with a class template in it with no definitions for its instantiations' member functions, and then prohibited you from altering it? Quit your job today. – Lightness Races in Orbit Dec 01 '12 at 10:57
  • @BobJohn create another .cpp where you use your class – wimh Dec 01 '12 at 10:57
  • Oh, it includes a .cpp. Yikes! – Lightness Races in Orbit Dec 01 '12 at 10:57
  • @BobJohn Just compile! If no code uses the templates, then you will get no compiler errors. So if you are not sure whether code uses it, make some test code that instantiates `DList`. In fact, you should write the test code first :-) – juanchopanza Dec 01 '12 at 10:58
  • 1
    @BobJohn: He means don't compile it as a unit in its own right -- its code will already be compiled along with whatever code has recursively `#include`d it as a header, and shouldn't be built separately. – Lightness Races in Orbit Dec 01 '12 at 11:01
  • @LightnessRacesinOrbit I see, but don't they both have to be part of the same project for the program to even run? How do I get around just compiling .h? – Bob John Dec 01 '12 at 11:03
  • @BobJohn no, they don't have to be part of the same "project" (there is no such concept at the C++ language level). You don't need your code to be part of the standard C++ library in order to use `std::vector`. You just include it and use it, and if there were a bug in the implementation you would soon find out. – juanchopanza Dec 01 '12 at 11:16
  • @BobJohn: The concept of .cpps and .hs is just a convention (that's assumed by some IDEs), but the idea is that you (1) **compile** each individual `.cpp` file (or translation unit) with all the `.hpp` files that it `#include`s being incorporated implicitly, then (2) **link** the results of these individual compilations together to produce the final binary. In this instance, you will be removing your `.cpp` from step 1 because in fact its contents will instead be built implicitly as part of having been `#included` in some _other_ `.cpp`. – Lightness Races in Orbit Dec 01 '12 at 11:32
  • (Hence it won't really act like every other `.cpp` any more and shouldn't have been called this.) – Lightness Races in Orbit Dec 01 '12 at 11:34
2

The header file is defined for me, I'm not allowed to change it in any way

Then you'll need to remove the #include "dlist.h" directive from the .cpp (because you're already in dlist.h, and thus creating a cyclic dependency), thus making everything completely backwards, because the header you were given is fscking stupid!

A .cpp should never be #included. Typically, if one must split template-related definitions into their own file, it should have some other extension. I strongly recommend you talk to the person who has emplaced this mandate on you and explain that their header is silly, confusing and non-conventional.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Here is the note provided: Note: the following line is included *only* because the g++ compiler needs to see the "templatized" versions of your methods. This is the *only* instance in which it is acceptable to #include a cpp file. – Bob John Dec 01 '12 at 11:01
  • 1
    @BobJohn: Well, the note is wrong. It is _not_ acceptable. The file should have an extension other than `.cpp`. – Lightness Races in Orbit Dec 01 '12 at 11:03
  • Interesting. Can you elaborate on why this is the case? I'm curious to know why it's so widely frowned upon. – Bob John Dec 01 '12 at 11:04
  • @BobJohn: For precisely this reason. Because you're including the `.cpp` from within a header, you need to be careful _not_ to include the `.hpp` from within the `.cpp`, and also configure (possibly _hack_!) your build system not to compile the `.cpp` as a translation unit. This is a 100% reversal of the common convention and will be surprising to any first-time maintainer of your code. Had the file had an exception like `.ipp` (or something else), I doubt you'd ever have had the reflex to `#include "dlist.h"` in the first place, and thus your problem would never even have occured. – Lightness Races in Orbit Dec 01 '12 at 11:16
1

Instead of using #include "dlist.cpp" in your header file, move the function definition into dlist.h.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
1

Why do you include .cpp file in .h file? In 99% of cases you shouldn't do that.

Just add your code

template <typename T>
bool Dlist<T>::isEmpty() const
{
    return !first and !last;
}

Instead of .cpp file include directive.

0

Remove the #include "dlist.h" and do not compile dlist.cpp itself.

You can also use something like this:

because dlist.h includes dlist.cpp and defines __DLIST_H__:

#define __DLIST_H__

You can modify the dlist.cpp to

#ifdef __DLIST_H__

template <typename T>
bool Dlist<T>::isEmpty() const
{
    return !first and !last;
}

#endif

This way you don't get a compiler error if you try to compile dlist.cpp. But I agee with the other answers it is better not to name this file .cpp.

Community
  • 1
  • 1
wimh
  • 15,072
  • 6
  • 47
  • 98