使用私有析构函数绕过模板错误

时间:2016-05-27 09:04:20

标签: c++ templates

在编译时,我遇到了以下问题,如何进行编译,因为从概念上讲,这是正确的,任何重构建议都是受欢迎的。

我收到了编译错误,因为"搜索"析构函数是私有的,但我不会在搜索指针上使用delete,因为我在基类的初始化中提供了自定义Deleter。我知道编译器不知道如何绕过它。

错误说明: 错误C2248:无法访问在课程中声明的私人成员'搜索' 编译器已生成'搜索::〜搜索'这里

class Search
{
public:
static Search* New(/* */);    // using a pool of already allocated objects to avoid expensive allocations
    static void Delete(Search*);

private:
    Search(/* */) {/* */}
    ~Search() {/* */}
};


template<class T>
class MyList
{
    public:
    typedef (*CustomDeleter) (T* pElement);

    MyList(CustomDeleter lpfnDeleter = NULL) {};


    void Empty()
    {
        for (/**/)
         {
            if (m_pList[m_nListLastUsed])
            {
               if (m_lpfnCustomDeleter == NULL)
                  delete m_pList[m_nListLastUsed]; // COMPILE ERROR HERE BECAUSE Search destructor is private BUT I won't use that instruction since
                                                    // I provided a custom Deletern I know that the compiler doesn't know that, how to bypass it
               else
                  m_lpfnCustomDeleter(m_pList[m_nListLastUsed]);
            }
         }
    }

    private:
    T** m_pList;
    CustomDeleter m_lpfnCustomDeleter;  // Pointer to a custom deleter
};

class Query : public MyList<Search>
{
    public:
    Query() : MyList<Search>(&Search::Delete) // I set a custom deleter since Search hides its destructor : is this the right way ?
    {}

    ~Query()
    {
        /****/
        Empty(); // PROBLEM HERE
        /***/
    }
};

2 个答案:

答案 0 :(得分:3)

确保&#39; m_lpfnCustomDeleter&#39;永远不是NULL或更好的nullptr。你可以通过回到默认的“删除者”来确保这一点。如果用户没有提供任何自定义删除器。

我更喜欢下面的内容。

#include <iostream>

template <typename PointerType>
struct DefaultDeleter {
  void operator()(PointerType* ptr) {
    std::cout << "Delete\n";
  }
};

struct CustomDeleter {
  void operator()(int* ptr) {
    std::cout << "Custom int deleter" << std::endl;
  }
};

template <typename T, typename Deleter = DefaultDeleter<T>>
class Whatever
{
public:
  Whatever() {
    std::cout << "Cons\n";
  }
  void deinit() {
    Deleter d;
    auto v = new T;
    d(v); // Just for the sake of example
  }
};

int main() {
  Whatever<char> w;
  w.deinit();

  Whatever<int, CustomDeleter> w2;
  w2.deinit();
  return 0;
}

更新::无代码重构 假设没有c ++ 11

  1. 将这个小的元程序添加到您的代码库中。

    命名空间{{/ p>

          template <typename T, typename U> struct is_same {
            static const bool value = false;
          };
    
          template <typename T>
          struct is_same<T, T> {
            static const bool value = true;
          };
    
          template <bool v, typename T = void> struct enable_if;
    
          template <typename T = void> struct<true, T> {
            typedef T type;
          };  
    
        }
    
  2. 将空功能更改为:

    void Empty(){   for(/ **** /){     do_delete();   } }

  3.     template <typename =
                      typename my::enable_if<my::is_same<T, Search>::value>::type>
        void do_delete() {
          assert (m_lpfnCustomDeleter != NULL);
          m_lpfnCustomDeleter(m_pList[m_nListLastUsed]);
        }
    
        void do_delete() {
          delete m_pList[m_nListLastUsed];
        }
    

    如果您使用的是c ++ 11,那么您就不必在命名空间&#39; my&#39;下编写元节目。只需替换我的:: is_same&#39;和我的:: enable_if&#39;与&#39; std :: is_same&#39;和&#39; std :: enable_if&#39;。

    注意:,尚未编译和测试上述代码。

答案 1 :(得分:1)

将执行删除的代码与其余代码分开:

        if (m_pList[m_nListLastUsed])
        {
           if (m_lpfnCustomDeleter == NULL)
              delete m_pList[m_nListLastUsed]; // COMPILE ERROR HERE BECAUSE Search destructor is private BUT I won't use that instruction since
                                                // I provided a custom Deletern I know that the compiler doesn't know that, how to bypass it
           else
              m_lpfnCustomDeleter(m_pList[m_nListLastUsed]);
        }

通过调用以下代码替换上面的代码:

custom_delete(m_pList[m_nListLastUsed]);

然后将其添加为列表类的方法,不要忘记包含<type_traits>

std::enabled_if<std::is_destructible<T>::value, void>::type custom_delete(T* ptr) {
    /* Note: this isn't pre-2000 anymore, 'lpfn' as a prefix is horrible,
       don't use prefixes! */
    if (m_lpfnCustomDeleter) {
       m_lpfnCustomDeleter(ptr);
    } else {
       delete ptr;
    }
}

std::enabled_if<!std::is_destructible<T>::value, void>::type custom_delete(T* ptr) {
     if (!m_lpfnCustomDeleter) {
          throw "No custom deleter for a non destructible type!";
     }
     m_lpfnCustomDeleter(ptr);
}

enabled_if将使得如果对象具有私有析构函数,则列表中不存在对象delete直接存在的函数。

或者,您可以将充当自定义删除器的结构(或函数)作为列表的第二个模板参数传递,默认值为调用delete运算符的值,然后直接在指针上调用此结构,如Arunmu的anser。