1

I wrote an abstract class called "List", which I basically use as an interface for other implementation methods of lists (such as dynamic array and linked list).

Now, I have a method called "add", with 2 arguments, the first one is the data to add and the second is the position where to add. I want to set the default value of this position to be the end of the list (i.e, its size).

Here's what I wrote:

#ifndef LIST_H
#define LIST_H

template <class T>
class List
{
    public:
        List(): m_size(0) {}
        virtual bool isEmpty() const=0;
        virtual void set(int index, T value)=0;
        virtual int getSize() const=0;
        virtual void add(T data, int index = m_size )=0;        //The problem is here    
        virtual T remove(int index)=0;
        virtual ~List(){}
        virtual T operator[](int index) const =0;
        virtual bool operator==(const List<T>& other) const =0;

    protected:

        int m_size;

};
#endif // LIST_H

But I get the following error:

error: invalid use of non-static data member 'List<T>::m_size'|

Is there a way to fix this problem?

Also, is it correct to implement the constructor as I did? do I need to call this constructor in the init line of the derived classes constructors or does the compiler does it implicitly?

Thanks in advance.

  • Have a non-abstract overload that only takes one argument, and calls the proper `add` function with the `m_size` variable? (E.g. `void add(T data) { add(data, m_size); }`) – Some programmer dude Jan 26 '22 at 11:39
  • 1
    With that said, why do you want a member variable in an otherwise almost abstract class? IMO you should either make the class fully abstract, declaring only the `List` *interface* (without constructors, destructor, or member variables); *Or* you make it a concrete class with the member functions being implemented. If you really want the class to be an abstract interface, then let the concrete implementations of it deal with size etc. – Some programmer dude Jan 26 '22 at 11:41

3 Answers3

4

You cannot use members as default arguments of methods. Instead, write an overload. It doesn't need to be virtual when it calls the virtual add(T,int):

void add(T data) {
     add(data,m_size );
}
Jeff Schaller
  • 2,352
  • 5
  • 23
  • 38
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • If I want to obligate the derived classes to implement this add overload, should I declare it as a pure virtual? – DirichletIsaPartyPooper Jan 26 '22 at 11:49
  • @DirichletIsaPartyPooper I don't understand why you want that, but yes it can be overriden in derived classes, but then it isnt necessarily the same as a default argument to `add(T,int)` – 463035818_is_not_an_ai Jan 26 '22 at 11:54
  • Of if you want to make it non-overridable wrapper for just providing the default size, you can always make it `virtual final`, i.e. `virtual void add(T data) final {add(data, m_size);}`, then it won't be possible to override in derived class. – alagner Jan 26 '22 at 11:59
  • The better solution would be to implement this method in the base class and call from the implementation to the virtual add, although my base class is abstract (and therefore maybe its not the best programming to implement methods there?) – DirichletIsaPartyPooper Jan 26 '22 at 12:00
  • Better to rename the virtual to avoid the hiding of the method in derived class (overload (and default argument too) with virtual might surprise)... – Jarod42 Jan 26 '22 at 12:06
1

As you may know, all class instances share the same VTABLE - table of virtual methods. Given that, you can't introduce instance-specific knowledge to that shared source. Just imagine you have two lists of the same type, first one with m_size=10 and another one with m_size=42. How to resolve that conflict? What to do if m_size is changed over time? etc.

So, it's may be worth to introduce two methods, like insert(data, pos) and append(data).

Yury Schkatula
  • 5,291
  • 2
  • 18
  • 42
1

The problem is that you are using m_size as a default parameter for your implementation. As pointed out here, it is not possible to do that straight away. What you could do is overload this function as follows:

virtual void add(T data, int index)=0     // Original - remove the default parameter
void add(T data)                          // Overload - call with m_size
{
    add(data, m_size);
}

This way, all you have to do is override the original function.

JeanLColombo
  • 160
  • 6