Неоднозначный вызов оператора [] внутри вложенного класса

Я пытаюсь создать структуру данных, которая имеет векторные функции для изучения C++. В настоящее время я застрял, пытаясь скомпилировать код, подобный этому:

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();
    }
};

Я получаю неоднозначный вызов перегруженной функции на моем operator* функция внутри моего вложенного класса. Хотя я думаю, что я получаю проблему (как компилятор узнает, если это его rhs/lhs), я не совсем уверен, как это исправить. Я хочу использовать operator[] Функция для этого.

Я ожидаю, что эти две строки будут печатать одно и то же:

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

Любой совет?

1 ответ

Решение

operator[] что вы определили для вашего TestClass никоим образом не "унаследован" или как-то "встроен" в ваш вложенный класс.

Вы можете думать о вложенных классах C++ как об обычных классах, которые живут в "вложенном пространстве имен": TestClass::NestedClass в вашем примере кода.

Если вы хотите operator[] для вашего вложенного класса, вы должны определить один с нуля (как вы сделали для вашего TestClass).


РЕДАКТИРОВАТЬ 1: Возвращая классы (C++ не Java)

Также обратите внимание, что C++ не похож, например, на Java (с шаблоном распределения экземпляров классов с new и полагаться на сборщик мусора для автоматического сбора "мусора").
Итак, код такой:

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

не должен даже компилироваться.

Если вы вернули NestedClass Экземпляр динамически распределяется с new, вы должны вернуть указатель на него, например:

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

Но это скорее шаблон Java.

В C++ вы можете просто захотеть вернуть экземпляр NestedClass без new динамическое распределение; это должно работать просто отлично:

   NestedClass newNestedClass() {
        return NestedClass();
   }

РЕДАКТИРОВАТЬ 2: Правильное управление ресурсами

Обратите внимание, что вы можете сделать T* data; член private для лучшей инкапсуляции и сокрытия информации. Вы предоставляете надлежащие общедоступные средства доступа (например, operator[] перегрузки) для доступа к данным вашего класса: предоставьте доступ к функциям-членам средства доступа, а не к членам-данным.

Более того, вы динамически выделяетесь data в кучу, используя new, Вы должны освободить динамически выделенную кучу памяти, чтобы избежать утечек памяти (и ресурсов).
Хорошее место для этого - деструктор вашего класса, например:

class TestClass {
private:
    T* data;

public:
    ...

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

Обратите внимание, что этот код:

data = new T{t};

просто динамически выделяет один экземпляр T, инициализируя его значением t,

Соответствующий код очистки:

delete data;

Однако, если вы хотите динамически выделить массив T s, синтаксис:

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

и соответствующий синтаксис очистки:

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

Также обратите внимание, что если вы хотите вручную управлять ресурсами в своем классе, вы должны рассмотреть также определение конструктора копирования и назначения копирования operator=() (см. так называемое правило трех); и если вы хотите реализовать семантику перемещения, вам также следует рассмотреть возможность реализации конструктора перемещения и назначения перемещения (в этом случае есть соответствующее "Правило 5").

Но если вы полагаетесь на уже имеющихся менеджеров ресурсов RAII, как std::vector вам не нужно тратить время, энергию и на поиск ошибок, управляя ресурсами вручную: все это автоматически управляется std::vector или любой другой контейнерный класс, который вы выберете (в этом случае у вас есть простое "Правило нуля":), то есть конструктор копирования, сгенерированный компилятором по умолчанию, оператор присваивания копии, конструктор перемещения, оператор присваивания перемещения и деструктор сделают трудную задачу.

Но, конечно, если это учебное упражнение, вы можете реализовать эти специальные функции-члены самостоятельно.

Другие вопросы по тегам