C++ 如何更好地实现此autoscope模板对象?

C++ 如何更好地实现此autoscope模板对象?,c++,c++11,visual-c++,C++,C++11,Visual C++,我有一个自动作用域类型的想法,当资源离开作用域或包含的对象被破坏时,它将清理资源 这就是目前的情况: template <typename RESOURCE, typename DELETEOR> struct autoscope { RESOURCE m_resource; DELETEOR const& m_deleteor; autoscope(RESOURCE resource, DELETEOR const& deleteor)

我有一个自动作用域类型的想法,当资源离开作用域或包含的对象被破坏时,它将清理资源

这就是目前的情况:

template <typename RESOURCE, typename DELETEOR>
struct autoscope
{
    RESOURCE m_resource;
    DELETEOR const& m_deleteor;

    autoscope(RESOURCE resource, DELETEOR const& deleteor)
        : m_resource(resource)
        , m_deleteor(deleteor)
    {
    }

    operator RESOURCE()
    {
        return m_resource;
    }

    ~autoscope()
    {
        m_deleteor(m_resource);
    }
};

template <typename RESOURCE, typename DELETEOR>
autoscope<RESOURCE, DELETEOR> make_autoscope(RESOURCE resource, DELETEOR deleteor)
{
    return autoscope<RESOURCE, DELETEOR>(resource, deleteor);
}

这需要做很多工作

  • 删除程序绝对不应存储为引用。这是一个灾难的配方,即使你修正了
    make_autoscope
    也通过引用删除器。使用lambda作为删除程序调用
    make_autoscope
    ,代码就会崩溃
  • 复制和移动操作需要修复。默认编译器生成的版本肯定不适合。这个类不应该是可复制的,必须是可移动构造的(为了
    make\u autoscope
    工作),并且可能需要也可能不需要是可移动分配的
  • 该接口可以使用一些额外的成员,如
    get()
    reset()
    ,和
    release()
  • 我不确定担心东西的大小有多大意义,但是如果需要,可以将删除器和资源存储在压缩对中。至于内联,如果deleter是一个函数指针,并且对象被移动,编译器可能无法内联调用。但这并不是什么新鲜事

    • 这需要大量的工作

      • 删除程序绝对不应存储为引用。这是一个灾难的配方,即使你修正了
        make_autoscope
        也通过引用删除器。使用lambda作为删除程序调用
        make_autoscope
        ,代码就会崩溃
      • 复制和移动操作需要修复。默认编译器生成的版本肯定不适合。这个类不应该是可复制的,必须是可移动构造的(为了
        make\u autoscope
        工作),并且可能需要也可能不需要是可移动分配的
      • 该接口可以使用一些额外的成员,如
        get()
        reset()
        ,和
        release()
      • 我不确定担心东西的大小有多大意义,但是如果需要,可以将删除器和资源存储在压缩对中。至于内联,如果deleter是一个函数指针,并且对象被移动,编译器可能无法内联调用。但这并不是什么新鲜事

      c++11在
      unique\u ptr
      shared\u ptr(T*,Deleter())
      中已经具备了您所需要的功能

      没有任何理由在这里重新发明轮子

      例如:

      #包括
      #包括
      #包括
      auto main()->int
      {
      自动文件\u closer=[](文件*fp)->int
      {
      返回std::fclose(fp);
      };
      //唯一的自定义删除器匹配int fclose()的签名
      使用unique_auto_file=std::unique_ptr;
      唯一的自动文件f(fopen(“temp.txt”,“r”),文件f);
      //要归档的共享\u ptr
      使用shared_auto_file=std::shared_ptr;
      //使用自定义删除器构造(忽略int)
      共享的自动文件fs(fopen(“temp.txt”,“r”),文件关闭器);
      返回0;
      }
      

      注意:由于@PeterSom的有用评论而更新的c++11已经有了
      唯一的ptr
      共享的ptr(T*,Deleter())
      中所需的内容

      没有任何理由在这里重新发明轮子

      例如:

      #包括
      #包括
      #包括
      auto main()->int
      {
      自动文件\u closer=[](文件*fp)->int
      {
      返回std::fclose(fp);
      };
      //唯一的自定义删除器匹配int fclose()的签名
      使用unique_auto_file=std::unique_ptr;
      唯一的自动文件f(fopen(“temp.txt”,“r”),文件f);
      //要归档的共享\u ptr
      使用shared_auto_file=std::shared_ptr;
      //使用自定义删除器构造(忽略int)
      共享的自动文件fs(fopen(“temp.txt”,“r”),文件关闭器);
      返回0;
      }
      

      注意:由于@PeterSom在
      make_autoscope
      中的有用注释

      而更新,您将函数参数(按值传递)绑定到引用,因此返回了一个悬而未决的参考。
      HICON
      事件让我想起了这篇ACCU文章:你应该能够用
      unique\u ptr
      @Praetorian替换
      autoscope
      ,这是有问题的<例如,code>HICON可能不是指针类型。@dyp更一般地说,隐藏引用是个糟糕的主意。任何时候使用临时(例如,lambda表达式)调用它,都会得到一个悬空引用。在
      make_autoscope
      中,您将函数参数(通过值传递)绑定到引用,因此返回了一个悬而未决的参考。
      HICON
      事件让我想起了这篇ACCU文章:你应该能够用
      unique\u ptr
      @Praetorian替换
      autoscope
      ,这是有问题的<例如,code>HICON可能不是指针类型。@dyp更一般地说,隐藏引用是个糟糕的主意。任何时候使用临时(例如,lambda表达式)调用它时,您都会得到一个悬空引用。“任何使用lambda作为删除器调用_autoscope的调用,您的代码都会爆炸。”UB,但如果lambda是无状态的,则可能不会引起注意。“任何使用lambda作为删除器调用_autoscope的调用,您的代码都会爆炸。”UB,但是如果lambda是无状态的,它可能不会引起注意。发明轮子有很多理由,从资源不一定是指针这一事实出发……在这种情况下,普通的RAII封装将是足够的、简单的和简洁的。虽然我对答案投了赞成票,但它包含未定义的行为,因为C++标准不允许接受标准库函数的地址。它需要包装在deleter函数(对象)中。请参见C++2a中的R>=3),这将更清楚地表明它是verboten。标准库实现可以为任何标准库函数提供更多重载或带有默认参数的附加参数。只有这些调用才需要工作(只有少数例外情况)。@P
      template <typename RESOURCE, typename CRTP, RESOURCE invalid>
      struct autoscope
      {
      protected:
          RESOURCE m_res;
      public:
          autoscope()
              : m_res(invalid)
          {
          }
      
          autoscope(RESOURCE res)
              : m_res(res)
          {
          }
      
          autoscope(autoscope&& move)
          {
              std::swap(move.m_res, m_res);
          }
      
          autoscope(autoscope& copy) = delete;
      
          operator RESOURCE() const
          {
              return m_res;
          }
      
          RESOURCE get() const
          {
              return m_res;
          }
      
          RESOURCE operator =(RESOURCE res)
          {
              set(res);
          }
      
          void set(RESOURCE res)
          {
              static_cast<CRTP*>(this)->delete_resource();
              m_res = res;
          }
      
          RESOURCE release()
          {
              RESOURCE result = m_res;
              m_res = invalid;
              return result;
          }
      
          ~autoscope()
          {
              static_cast<CRTP*>(this)->delete_resource();
          }
      
          operator bool() const
          {
              return m_res != (RESOURCE)0;
          }
      
          bool valid() const
          {
              return m_res != invalid;
          }
      };
      
      template <typename RESOURCE, BOOL(WINAPI *fn_delete)(RESOURCE), RESOURCE invalid = (RESOURCE)-1>
      struct autoscope_bool : public autoscope<RESOURCE, autoscope_bool<RESOURCE, fn_delete, invalid>, invalid>
      {
          typedef autoscope<RESOURCE, autoscope_bool, invalid> base;
          autoscope_bool()
              : base(invalid)
          {
          }
      
          autoscope_bool(RESOURCE res)
              : base(res)
          {
          }
      
          autoscope_bool(autoscope_bool&& move)
              : base(std::forward<autoscope_bool>(move))
          {
          }
      
          autoscope_bool(autoscope_bool& copy) = delete;
      
          void delete_resource()
          {
              if (base::m_res && base::m_res != invalid)
              {
                  VERIFY(fn_delete(base::m_res));
                  base::m_res = invalid;
              }
          }
      };
      
      template <typename RESOURCE, typename void(WINAPI *fn_delete)(RESOURCE), RESOURCE invalid = (RESOURCE)-1>
      struct autoscope_void : autoscope<RESOURCE, autoscope_void<RESOURCE, fn_delete, invalid>, invalid>
      {
          typedef autoscope<RESOURCE, autoscope_void, invalid> base;
          autoscope_void()
              : base(invalid)
          {
          }
      
          autoscope_void(RESOURCE res)
              : base(res)
          {
          }
      
          autoscope_void(autoscope_void&& move)
              : base(std::forward<autoscope_void>(move))
          {
          }
      
          autoscope_void(autoscope_void& copy) = delete;
      
          void delete_resource()
          {
              if (m_res != invalid)
              {
                  VERIFY(fn_delete(m_res));
                  m_res = invalid;
              }
          }
      };
      
      template <typename RESOURCE, typename FUNCTOR_DELETE, RESOURCE invalid = (RESOURCE)-1>
      struct autoscope_functor : autoscope<RESOURCE, autoscope_functor<RESOURCE, FUNCTOR_DELETE, invalid>, invalid>
      {
          typedef autoscope<RESOURCE, autoscope_functor, invalid> base;
      private:
          FUNCTOR_DELETE m_functor_delete;
      public:
          autoscope_functor(FUNCTOR_DELETE functor_delete)
              : autoscope(invalid)
              , m_functor_delete(functor_delete)
          {
          }
      
          autoscope_functor(RESOURCE res, FUNCTOR_DELETE functor_delete)
              : autoscope(res)
              , m_functor_delete(functor_delete)
          {
          }
      
          autoscope_functor(autoscope_functor&& move)
              : base(std::forward<autoscope_functor>(move))
              , m_functor_delete(move.m_functor_delete)
          {
          }
      
          autoscope_functor(autoscope_functor& copy) = delete;
      
          void delete_resource()
          {
              if (m_res != invalid)
              {
                  m_functor_delete(m_res);
                  m_res = invalid;
              }
          }
      };
      
      #include <iostream>
      #include <cstdio>
      #include <memory>
      
      auto main() -> int
      {
      
          auto file_closer = [](FILE* fp) -> int
          {
              return std::fclose(fp);
          };
      
          // a unique_ptr who's custom deleter matches the signature of int fclose()
          using unique_auto_file = std::unique_ptr<FILE, int (*)(FILE*)>;
          unique_auto_file f(fopen("temp.txt", "r"), file_closer);
      
          // a shared_ptr to FILE
          using shared_auto_file = std::shared_ptr<FILE>;
          // construct with custom deleter (int-ignored) 
          shared_auto_file fs(fopen("temp.txt", "r"), file_closer);
      
          return 0;
      }