0

I am trying to make an ArrayList class in C++ as a learning experience. Similar to the Java ArrayList class it is a dynamic array, the internal array is called 'content'. Unlike in Java I wanted my ArrayList to be able to take pointers and non-pointers. But I am getting a problem, I am using std::is_pointer::value to check whether the generic type is a pointer within a number of functions. If it is a pointer the function will need to preform differently to if it was not a pointer. My example below shows my printAll() function which is meant to print every element in the array on separate lines. If the generic type is a pointer the method needs to dereference each element in the array before printing them.

   int main() {
        ArrayList<int> a = ArrayList<int>();
        a.add(1);
        a.add(2);
        a.add(3);
        a.printAll();
        cin.ignore();
    }

    #pragma warning(push)
    #pragma warning(disable : 2100)
    template<class T>
    void ArrayList<T>::printAll() {
        if (std::is_pointer<T>::value) {
            for (int i = 0; i < this->occupiedSize; i++) {
                cout << "[" << i << "] " << *this->content[i];
                cout << endl;
            }
        } else {
            for (int i = 0; i < this->occupiedSize; i++) {
                cout << "[" << i << "] " << this->content[i];
                cout << endl;
            }
        }
    }
    #pragma warning(pop)

On the line: cout << "[" << i << "] " << *this->content[i]; I am getting warnings: C2100: illegal indirection and C2088: '<<': illegal for class

I'm assuming this is because the list is not of a pointer type and therefore cannot be dereferenced with *. But in a non-pointer list, std::is_pointer::value should return false and the code in that block is never executed anyway so it shouldn't be a problem. Disabling the warning doesn't seem to help.

If I do:

int main() {
    ArrayList<int*> a = ArrayList<int*>();
    a.add(new int(1));
    a.add(new int(2));
    a.add(new int(3));
    a.printAll();
    cin.ignore();
}

It works totally fine. Any ideas how I can solve this problem or how I can better implement this functionality?

I am using windows 10, Microsoft Visual Studio (latest version I believe).

Ollie Kampo
  • 38
  • 1
  • 3
  • 1
    "Disabling the warning doesn't seem to help" - well duh! Of course disabling it does not "help" - that just makes the compiler shut up about it, it doesn't actually *change* anything. Your code is just as broken, now the compiler just doesn't tell you. – Jesper Juhl Jul 21 '17 at 14:16
  • I get that, but how to I fix it? Whats the way around this? – Ollie Kampo Jul 21 '17 at 14:18
  • Do what the C++ library does, and leave printing to the user. – molbdnilo Jul 21 '17 at 14:19
  • Please post a [mcve] so we can see *all* relevant code. But, my crystal ball says that `cout << "[" << i << "] " << *this->content[i];` should really be `cout << "[" << i << "] " << this->content[i];` ... – Jesper Juhl Jul 21 '17 at 14:21
  • That would solve it yeah. But I really wanted to know if it is possible for this kind of behaviour to be implemented. The class also have a sort function which also requires the same behaviour as the printAll() function, otherwise for lists of pointers it would sort by memory location. – Ollie Kampo Jul 21 '17 at 14:23

1 Answers1

2

This issue here is that even though you would never enter

if (std::is_pointer<T>::value) {
    for (int i = 0; i < this->occupiedSize; i++) {
        cout << "[" << i << "] " << *this->content[i];
        cout << endl;
    }
}

If T is a non pointer type the compiler will still compile that block of code. Since it has illegal syntax you get a compiler warning.

In the upcoming C++17 you can use constexpr if which will remove the code from the compilation if the condition is not true.

If you do not have access to a compiler that supports that feature then you will have to use SFINAE and have two overloads of the function. One for if T is a pointer type and one for when T is not a pointer type. That would look like

template<class T, std::enable_if_t<typename std::is_pointer<T>::value>* = nullptr>
void ArrayList<T>::printAll() {
    for (int i = 0; i < this->occupiedSize; i++) {
        cout << "[" << i << "] " << *this->content[i];
        cout << endl;
    }
}

template<class T, std::enable_if_t<typename !std::is_pointer<T>::value>* = nullptr>
void ArrayList<T>::printAll() {
    for (int i = 0; i < this->occupiedSize; i++) {
        cout << "[" << i << "] " << this->content[i];
        cout << endl;
    }
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • This works but only if I define the function inside the header file. If I define the function in the cpp file it says: unable to match function definition to an existing declaration. Any ideas? – Ollie Kampo Jul 21 '17 at 16:08
  • @OllieKampo You can't define template functions in a cpp file. see [this](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – NathanOliver Jul 21 '17 at 16:09
  • Ok, thank you so much for your help! Very nice answers :) – Ollie Kampo Jul 21 '17 at 16:25