代码之家  ›  专栏  ›  技术社区  ›  Evan Teran

具有不可复制类的模板类型推导

  •  2
  • Evan Teran  · 技术社区  · 14 年前

    假设我有一个autolocker类,它看起来像这样:

    template <T>
    class autolocker {
    public:
        autolocker(T *l) : lock(l) {
            lock->lock();
        }
    
        ~autolocker() {
            lock->unlock();
        }
    private:
        autolocker(const autolocker&);
        autolocker& operator=(const autolocker&);
    private:
        T *lock;
    };
    

    显然,目标是能够在任何具有锁定/解锁方法的情况下使用此自动锁定器,而无需借助虚拟函数。

    目前,它非常简单,可以这样使用:

    autolocker<some_lock_t> lock(&my_lock); // my_lock is of type "some_lock_t"
    

    但这样做是违法的:

    autolocker lock(&my_lock); // this would be ideal
    

    有没有得到模板类型扣除玩得很好(保持在我的自动锁是不可复制的)。或者仅仅指定类型是最简单的方法吗?

    4 回复  |  直到 14 年前
        1
  •  5
  •   Community c0D3l0g1c    7 年前

    是的,你可以使用范围保护技术

    struct autolocker_base {
        autolocker_base() { } 
    protected:
        // ensure users can't copy-as it
        autolocker_base(autolocker_base const&) 
        { }
    
        autolocker_base &operator=(autolocker_base const&)
        { return *this; }
    };
    
    template <T>
    class autolocker : public autolocker_base {
    public:
        autolocker(T *l) : lock(l) {
            lock->lock();
        }
    
        autolocker(const autolocker& o)
          :autolocker_base(o), lock(o.lock)
        { o.lock = 0; }
    
        ~autolocker() {
            if(lock)
              lock->unlock();
        }
    
    private:
        autolocker& operator=(const autolocker&);
    
    private:
        mutable T *lock;
    };
    

    然后编写一个函数来创建自动锁定器

    template<typename T>
    autolocker<T> makelocker(T *l) {
      return autolocker<T>(l);
    }
    
    typedef autolocker_base const& autolocker_t;
    

    然后你可以这样写:

    autolocker_t lock = makelocker(&my_lock);
    

    一旦常量引用超出范围,就调用析构函数。它不需要是虚拟的。至少海湾合作委员会 optimizes this quite well .

    遗憾的是,这意味着您必须使您的locker对象可复制,因为您需要从maker函数返回它。但是旧对象不会尝试解锁两次,因为复制时它的指针设置为0,所以它是安全的。

        2
  •  2
  •   sbi    14 年前

    很明显你不能逃避 autolocker 作为模板,因为要将其用作类型,必须实例化模板才能获取类型。

    但是类型擦除可以用来做你想要的。将类模板转换为类,将其构造函数转换为成员模板。但是您必须动态地分配一个内部实现对象。
    更好的方法是,存储一个指向执行解锁的函数的指针,并将该函数作为模板化构造函数选择的模板的实例。沿着这些线的东西:

    // Comeau compiles this, but I haven't tested it. 
    class autolocker {
    public:
        template< typename T >
        autolocker(T *l) : lock_(l), unlock_(&unlock<T>) { l->lock(); }
    
        ~autolocker()                                    { unlock_(lock_); }
    private:
        autolocker(const autolocker&);
        autolocker& operator=(const autolocker&);
    private:
        typedef void (*unlocker_func_)(void*);
    
        void *lock_;
        unlocker_func_ unlock_;
    
        template <typename T>
        static void unlock(void* lock)                   { ((T*)lock)->unlock(); }
    };
    

    实际上我没有尝试过,而且语法可能是错误的(我不确定如何获取特定函数模板实例的地址),但我认为原则上这应该是可行的。也许有人来修理我的错误。

    我更喜欢这个,而不是范围保护,出于某种原因,我从来没有真正喜欢过它。

        3
  •  1
  •   jwismar    14 年前

    autolocker 是类模板,而不是类。你的“这将是理想的”显示了一些在C++中没有意义的东西。

        4
  •  1
  •   doublep    14 年前

    我认为JWSWMAR是正确的,你想要的是C++不可能的。然而, 类似的 (不是直接模拟)可以用C++ 0x构造,使用一些新的特征(rVals/移动和自动变量类型):

    #include <iostream>
    
    template <typename T>
    class autolocker_impl
    {
    public:
      autolocker_impl(T *l) : lock(l) {
        lock->lock();
      }
    
      autolocker_impl (autolocker_impl&& that)
        : lock (that.lock)
      {
        that.lock = 0;
      }
    
      ~autolocker_impl() {
        if (lock)
          lock->unlock();
      }
    private:
      autolocker_impl(const autolocker_impl&);
      autolocker_impl& operator=(const autolocker_impl&);
    private:
      T *lock;
    };
    
    template <typename T>
    autolocker_impl <T>
    autolocker (T* lock)
    {
      return autolocker_impl <T> (lock);
    }
    
    struct lock_type
    {
      void lock ()
      { std::cout << "locked\n"; }
      void unlock ()
      { std::cout << "unlocked\n"; }
    };
    
    int
    main ()
    {
      lock_type l;
      auto x = autolocker (&l);
    }