【问题标题】:How can I detect that a template is of pointer type?如何检测模板是指针类型的?
【发布时间】:2020-05-02 07:50:14
【问题描述】:

我的代码中有几个问题。第一个在变量“元素”中,只要发送模板的类的构造函数具有默认值,它对我来说效果很好,有没有办法跳过构造函数而不在类中放置默认值?另一个问题是在释放内存时,当 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;
    }

};

}

【问题讨论】:

标签: c++ templates linked-list nodes


【解决方案1】:
namespace Linked{
template <class T>
struct Nodo{
    T Element;
    Nodo<T> *Next = nullptr;
    ~Nodo(){
        if constexpr (std::is_pointer<T>::value)
            delete Element;
    }
};

您还应该考虑 T 是否是指向数组的指针。

【讨论】:

    【解决方案2】:

    您的代码的唯一语法问题是,您没有 #include &lt;type_traits&gt; 并且在 is_pointer&lt;T&gt;::value 之前忘记了 std::

    但是您尝试做的事情会引发所有权问题。当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 不应尝试释放尚未分配的内存。

    【讨论】:

      【解决方案3】:

      由于您没有指定 c++ 版本,我假设您使用的是最新的,现在是 C++17。最适合您现有代码的是使用if constexpr,我不会详细说明,因为还有其他好的答案。如果您卡在 C++14 或 C++11(或更糟糕的 03/98,在这种情况下您应该简单地升级),您将需要专门化您的模板。 (我会回到这个)

      然而,这段代码违反了 CppCoreGuidelines 之一:ES.24: Use a unique_ptr&lt;T&gt; to hold pointers 通过编写模板来检测原始指针并删除它,总是需要分配。因此,您的链接列表不能引用现有事物的某些子数据。正如 cmets 中已经提到的,如果用户想要清理内存,请使用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&lt;T&gt;);

      上述解决方案解决了 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; }
        };
      }
      

      还有一种方法可以专门化单个方法,但是我不太熟悉,请参阅Stack overflow: Template specialization of a single method from a templated class 了解更多详细信息。

      【讨论】:

        猜你喜欢
        • 2015-08-13
        • 1970-01-01
        • 1970-01-01
        • 2016-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多