Template

There was no detected errors on the code but there was still undefined reference problems:

MAIN.cpp:(.text+0x64): undefined reference to `listsavitch::operator<<(std::ostream&, listsavitch::List<int> const&)'

MAIN.cpp:(.text+0xca): undefined reference to `listsavitch::operator<<(std::ostream&, listsavitch::List<char> const&)'

list.cpp
//This is the implementation file: list.cpp
//This is the implementation of the class template named List.
//The interface for the class template List is in the header file list.h.
#ifndef LIST_CPP
#define LIST_CPP
#include <iostream>
#include <cstdlib>
#include "list.h"//This is not needed when used as we are using this file,
//but the #ifndef in list.h makes it safe.
using namespace std;
namespace listsavitch
{
//Uses cstdlib:
template<class ItemType>
List<ItemType>::List(int max) : max_length(max), current_length(0)
{
item = new ItemType[max];
}
template<class ItemType>
List<ItemType>::~List( )
{
delete [] item;
}
template<class ItemType>
int List<ItemType>::length( ) const
{
return (current_length);
}
//Uses iostream and cstdlib:
template<class ItemType>
void List<ItemType>::add(ItemType new_item)
{
if ( full( ) )
{
cout << "Error: adding to a full list.\n";
exit(1);
}
else
{
item[current_length] = new_item;
current_length = current_length + 1;
}
}
template<class ItemType>
bool List<ItemType>::full( ) const
{
return (current_length == max_length);
}
template<class ItemType>
void List<ItemType>::erase( )
{
current_length = 0;
}
//Uses iostream:
template<class ItemType>
ostream& operator <<(ostream& outs, const List<ItemType>& the_list)
{
for (int i = 0; i < the_list.current_length; i++)
outs << the_list.item[i] << endl;
return outs;
}
}//listsavitch
#endif // LIST_CPP Notice that we have enclosed all the template
// definitions in #ifndef... #endif.
[/code]

[MAIN]
//Program to demonstrate use of the class template List.
#include <iostream>
#include "list.h"
#include "list.cpp"
using namespace std;
using namespace listsavitch;
int main( )
{
List<int> first_list(2);
first_list.add(1);
first_list.add(2);
cout << "first_list = \n"
<< first_list;
List<char> second_list(10);
second_list.add('A');
second_list.add('B');
second_list.add('C');
cout << "second_list = \n"
<< second_list;
return 0;
}

[endMAIN]

[list.h]
//This is the header file list.h. This is the interface for the class List.
//Objects of type List can be a list of items of any type for which the operators
//<< and = are defined. All the items on any one list must be of the same type. A
//list that can hold up to max items all of type Type_Name is declared as follows:
// List<Type_Name> the_object(max);
#ifndef LIST_H
#define LIST_H
#include <iostream>
using namespace std;

namespace listsavitch
{
template<class ItemType>
class List
{
public:
List(int max);
//Initializes the object to an empty list that can hold up to
//max items of type ItemType.

~List( );
//Returns all the dynamic memory used by the object to the free store.

int length( ) const;
//Returns the number of items on the list.

void add(ItemType new_item);
//Precondition: The list is not full.
//Postcondition: The new_item has been added to the list.

bool full( ) const;
//Returns true if the list is full.

void erase( );
//Removes all items from the list so that the list is empty.

friend ostream& operator <<(ostream& outs,
const List<ItemType>& the_list);
//Overloads the << operator so it can be used to output the
//contents of the list. The items are output one per line.
//Precondition: If outs is a file output stream, then outs has
//already been connected to a file.
private:
ItemType *item; //pointer to the dynamic array that holds the list.
int max_length; //max number of items allowed on the list.
int current_length; //number of items currently on the list.
};
}//listsavitch
#endif //LIST_H
[end code/]
Last edited on
https://www.cplusplus.com/articles/jEywvCM9/
Please format your code for readability.
Yet another template in cpp file (all has to be in header file) and incldue cpp (bad idea)
@beginnerzzzzz,
There are some undesirable ways around it, but template'd functions are usually defined inside header files (or a file that gets #include'd in the header file). The file containing the definition is sometimes given extension .hpp instead (but people have their own preferences).

Please use code tags in future. If it hadn't been an interesting challenge I wouldn't even have had a look at it.

Here's one possible split into list.hpp and main.cpp. Compile with (e.g.) just
g++ main.cpp
(plus all the compiler options you feel like adding, but which just complicate the argument here).

An alternative way of splitting is to have the class declaration in a .h file and the implementation in a .hpp file (say) that gets #include'd in the .h file (only). Take your pick. To avoid the template'd ostream operator << problem, however, I've opted here to put its definition within the class declaration. There are probably other ways.

And, yeah, I've ended up with "using namespace std;" inside a header file - so expect the full wrath of the forum on my back.


list.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <cstdlib>
using namespace std;

namespace listsavitch
{
   template<class ItemType>
   class List
   {
   public:
      List(int max);
     ~List( );

      int length( ) const;

      void add(ItemType new_item);
      bool full( ) const;
      void erase( );

      friend ostream& operator <<(ostream& outs, const List<ItemType>& the_list)  // INSIDE class
      {
         for (int i = 0; i < the_list.current_length; i++)
            outs << the_list.item[i] << endl;
         return outs;
      }

   private:
      ItemType *item;
      int max_length;
      int current_length;
   };
}


// Implementation (which could be in a separate file that is #include'd into this header file only)
namespace listsavitch
{
   template<class ItemType>
   List<ItemType>::List(int max) : max_length(max), current_length(0)
   {
      item = new ItemType[max];
   }

   template<class ItemType>
   List<ItemType>::~List( )
   {
      delete [] item;
   }

   template<class ItemType>
   int List<ItemType>::length( ) const
   {
      return current_length;
   }

   template<class ItemType>
   void List<ItemType>::add(ItemType new_item)
   {
      if ( full( ) )
      {
         cerr << "Error: adding to a full list.\n";
         exit(1);
      }
      else
      {
         item[current_length] = new_item;
         current_length = current_length + 1;
      }
   }

   template<class ItemType>
   bool List<ItemType>::full( ) const
   {
      return current_length == max_length;
   }

   template<class ItemType>
   void List<ItemType>::erase( )
   {
      current_length = 0;
   }
}



main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include "list.hpp"          // template'd header file
using namespace std;
using namespace listsavitch;

int main( )
{
   List<int> first_list(2);
   first_list.add(1);
   first_list.add(2);
   cout << "first_list = \n" << first_list;

   List<char> second_list(10);
   second_list.add('A');
   second_list.add('B');
   second_list.add('C');
   cout << "second_list = \n" << second_list;
}


first_list = 
1
2
second_list = 
A
B
C

Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>

namespace listsavitch {
    template<class ItemType>
    class List {
    public:
        List(size_t max);
        ~List();

        // Function bodies to be provided if needed
        List(const List&) = delete;
        List(List&&) = delete;
        List& operator=(List) = delete;

        size_t length() const;

        void add(const ItemType& new_item);
        bool full() const;
        void erase();

        friend std::ostream& operator <<(std::ostream& outs, const List<ItemType>& the_list)  // INSIDE class
        {
            for (size_t i = 0; i < the_list.current_length; ++i)
                outs << the_list.item[i] << ' ';

            return outs;
        }

    private:
        ItemType* item {};
        size_t max_length {};
        size_t current_length {};
    };
}

// Implementation
namespace listsavitch {
    template<class ItemType>
    List<ItemType>::List(size_t max) : max_length(max), item(new ItemType[max] {}) {}

    template<class ItemType>
    List<ItemType>::~List() { delete[] item; }

    template<class ItemType>
    size_t List<ItemType>::length() const { return current_length; }

    template<class ItemType>
    void List<ItemType>::add(const ItemType& new_item) {
        if (full()) {
            std::cerr << "Error: adding to a full list.\n";
            exit(1);
        } else
            item[current_length++] = new_item;
    }

    template<class ItemType>
    bool List<ItemType>::full() const { return current_length == max_length; }

    template<class ItemType>
    void List<ItemType>::erase() { current_length = 0; }
}

int main() {
    listsavitch::List<int> first_list(2);
    first_list.add(1);
    first_list.add(2);
    std::cout << "first_list = \n" << first_list << '\n';

    listsavitch::List<char> second_list(10);
    second_list.add('A');
    second_list.add('B');
    second_list.add('C');
    std::cout << "second_list = \n" << second_list << '\n';
}

Topic archived. No new replies allowed.