1

I'm trying to make a data structure that has vector features to learn some C++. I'm currently stuck trying to compile code similar to this:

template<typename T>
class TestClass {
public:
    T* data;
    TestClass(const T& t) { 
        data = new T{ t };
    }
    ~TestClass(void) {}
    TestClass(const TestClass&) {}

    T& operator[](int k) { return *data; }
    const T& operator[](int k) const { return *data; }
    class NestedClass {
    public:
        NestedClass(void) {}
        ~NestedClass(void) {}
        T& operator*(void) { return operator[](0); }
    };

    NestedClass newNestedClass(void) {
        return new NestedClass();
    }
};

I'm getting an ambiguous call to overloaded function on my operator* function inside my nested class. Though I think I get the problem (how will the compiler know if its rhs/lhs) I'm not entirely sure how to fix it. I want to use the operator[] function for this.

I'm expecting these two print lines to print the exact same thing:

TestClass<int> t(1);
auto n = t.newNestedClass();
cout << t[0] << endl;
cout << *n << endl;
return 0;

Any advice?

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
kir
  • 581
  • 1
  • 6
  • 22
  • 2
    The problem is that `operator[]` is not a member of `NestedClass`. You have to provide an object argument yourself. E.g. save a pointer to `TestClass`. – Columbo Feb 23 '15 at 08:57
  • A nested class is just an ordinary class in a separate namespace, where the outer class provides the namespace. Other than that, it has no special connection to the outer class, like it is (I think) in Java. – Ulrich Eckhardt Feb 23 '15 at 09:02
  • Thank you! I wasn't familiar with that concept. – kir Feb 23 '15 at 09:05

1 Answers1

2

The operator[] that you defined for your TestClass is in no way "inherited" or somehow "embedded" in your nested class.

You can think of C++ nested classes as just ordinary classes, that live in a "nested namespace": TestClass::NestedClass in your sample code.

If you want an operator[] for your nested class, you have to define one from scratch (as you did for your TestClass).


EDIT 1: Returning Classes (C++ Is Not Java)

Note also that C++ is not like e.g. Java (with a pattern of allocating instances of classes with new, and relying on a garbage collector to automatically collect "garbage").
So, code like this:

   NestedClass newNestedClass(void) {
        return new NestedClass();
    }

should not even compile.

If you returned a NestedClass instance dynamically allocated with new, you should return a pointer to it, e.g.:

   // Note the "*" for the returned pointer
   NestedClass* newNestedClass() {
        return new NestedClass();
   }

But, this is more of a Java pattern.

In C++, you may just want to return an instance of NestedClass without a new dynamic allocation; this should work just fine:

   NestedClass newNestedClass() {
        return NestedClass();
   }

EDIT 2: Proper Resource Management

Note also that you may want to make the T* data; member private for better encapsulation and information hiding. You are providing proper public accessors (like operator[] overloads) to access your class's data: expose the accessor member functions, not the data members.

Moreover, you dynamically allocated data on the heap using new. You must release the dynamically heap-allocated memory, to avoid memory (and resource) leaks.
A good place to do that is your class destructor, e.g.:

class TestClass {
private:
    T* data;

public:
    ...

    ~TestClass() {
        // Release resoruces dynamically allocated
        delete data;
    }
}

Note also that this code:

data = new T{t};

just dynamically allocates a single instance of T, initializing it to the value t.

The corresponding cleanup code is:

delete data;

However, if you want to dynamically allocate an array of Ts, the syntax is:

data = new T[elementCount];
// ... initialize data to some value...

and the corresponding cleanup syntax is:

delete[] data; // Note the []!!

Note also that, if you want to manually manage resources in your class, you should consider also defining copy constructor and copy assignment operator=() (see the so called Rule of Three); and if you want to implement move semantics, you should also consider implementing move constructor and move assignment (in this case there's a corresponding "Rule of 5").

But if you rely on already available RAII resource managers, like std::vector, you don't have to spend time, energy, and bug hunting, managing resources manually: it's all automatically managed by std::vector, or whatever container class you choose (in this case, you have a simple "Rule of Zero" :) i.e. the default compiler-generated copy constructor, copy assignment operator, move constructor, move assignment operator and destructor will do the tight thing.

But, of course, if this is a learning excercise, you may want to implement those special member functions by yourself.

Community
  • 1
  • 1
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Thank you for the heads up on your edit section. I found this compile error after I've fixed my initial problem :) – kir Feb 23 '15 at 09:44