Как я могу определить, что шаблон имеет тип указателя? - PullRequest
0 голосов

У меня есть пара проблем в моем коде. Первый находится в переменной «Элемент», и он хорошо работает для меня, пока конструктор класса, который отправил шаблон его переменным, имеет значения по умолчанию, есть ли способ пропустить конструктор, не помещая значения по умолчанию в класс? И другая проблема заключается в том, что когда дело доходит до освобождения памяти, когда T относится к типу указателя, мне нужно будет выполнить удаление, но как только я помещаю код, я получаю сообщение об ошибке, есть ли другое решение, которое может мне помочь? Буду внимателен к вашим ответам, спасибо: D

namespace Linked{
template <class T>
struct Nodo{
    const bool isponter = is_pointer<T>::value;
    T Element;
    Nodo<T> *Next;
    Nodo(){
        this->Next = nullptr;
    }
    ~Nodo(){
        if(is_pointer<T>::value)
            delete Element;
    }

};

}

Ответы [ 3 ]

0 голосов
/ 02 мая 2020

Единственные проблемы с синтаксисом в вашем коде - это то, что вы не #include <type_traits> и не забыли std:: до is_pointer<T>::value.

Но то, что вы пытаетесь сделать, вызовет проблемы с владением. Когда Nodo содержит указатель, он не должен владеть объектом, на который указывает указатель. Таким образом, вы не можете просто delete этот указатель, так как вы даже не можете знать, куда он указывает. Рассмотрим следующие три случая, каждый из которых требует различной обработки, но у вас нет возможности определить, с каким случаем вы сталкиваетесь:

Nodo<int*> n1, n2, n3;
n1.Element = new int(1); // requires delete

n2.Element = new int[10]; // requires delete[], crashes with delete

int i = 0;
n3.Element = &i; // no delete at all, crashes with delete

Обычно за его освобождение отвечает тот, кто выделил объект в куче. Nodo не должен пытаться освободить память, которую он не выделил.

0 голосов
/ 03 мая 2020

Поскольку вы не указали версию c ++, я предполагаю, что вы используете самую последнюю версию, которая сейчас называется C ++ 17. Наиболее подходящим вариантом для вашего существующего кода является использование if constexpr, я не буду останавливаться на этом, поскольку есть и другие хорошие ответы на этот вопрос. Если вы застряли на C ++ 14 или C ++ 11 (или хуже 03/98, в этом случае вам следует просто обновить), вам нужно будет специализировать свой шаблон. (Я вернусь к этому)

Этот код, однако, нарушает одно из CppCoreGuidelines: ES.24: Use a unique_ptr<T> to hold pointers При написании вашего шаблона для обнаружения необработанных указателей и удаления его, всегда есть выделить. Следовательно, ваш связанный список не может ссылаться на некоторые субданные чего-то существующего. Как уже упоминалось в комментариях, если пользователи хотят очистить память, используйте std::unique_ptr. Пример:

namespace Linked{
  template <class T>
  struct Nodo{
    T Element;
    Nodo<T> *Next{nullptr};
    Nodo() = default;
    ~Nodo() = default;
  };
}

// Has ownership
auto node = Nodo<std::unique_ptr<int>>{};
node.element = std::make_unique<int>(42);

// Has ownership (to array of 42 elements)
auto node = Nodo<std::unique_ptr<int[]>>{};
node.element = std::make_unique<int[]>(42);

// No ownership
int value = 42;
auto node = Nodo<int>{};
node.element = &value;

При этом право собственности становится для звонящего абонентом и прозрачным для вас. (поскольку вам не нужно знать об этих массивах, об этом знает std :: unique_ptr). Возможно, вы захотите наложить некоторые ограничения на T, например, добавив static_assert(std::is_nothrow_move_constructable<T>);.

. Это решение выше решает проблему в C ++ 11 и выше и должен быть рекомендованным подходом.

Если нет, используйте if constexpr, если ваше условие не может быть записано в выделенном классе в C ++ 17. И частичная специализация на C ++ 14 и C ++ 11.

namespace Linked{
  template <class T>
  struct Nodo{
    T Element;
    Nodo<T> *Next{nullptr};
    Nodo() = default;
    ~Nodo() = default;
  };
  template <class T>
  struct Nodo<T*>{
    T *Element{nullptr};
    Nodo<T> *Next{nullptr};
    Nodo() = default;
    ~Nodo() { delete Element; }
  };
}

Если вы не хотите слишком много повторять свой код

namespace Linked{
  template <class T, class Me>
  struct AbstractNodo{
    T Element;
    Me *Next{nullptr};

    // All common code
  };
  template <class T>
  struct Nodo : AbstractNodo<T, Nodo<T>>{
    Nodo() = default;
    ~Nodo() = default;
  };
  template <class T>
  struct Nodo<T*> : AbstractNodo<T, Nodo<T*>>{
    Nodo() = default;
    ~Nodo() { delete Element; }
  };
}

Существует также способ Я специализируюсь на одном методе, однако я не очень знаком с ним, см. Переполнение стека: специализация шаблона для одного метода из шаблонного класса для получения более подробной информации.

0 голосов
/ 02 мая 2020
namespace Linked{
template <class T>
struct Nodo{
    T Element;
    Nodo<T> *Next = nullptr;
    ~Nodo(){
        if constexpr (std::is_pointer<T>::value)
            delete Element;
    }
};

Вы также должны учитывать, является ли T указателем на массив.

...