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

时间:2010-05-15 22:46:51

标签: c++ templates

假设我有一个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

无论如何都要获得模板类型演绎以便与此相配(保持在我的autolocker中是不可复制的)。或者只是指定类型最简单吗?

4 个答案:

答案 0 :(得分:5)

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

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);

一旦const引用超出范围,就会调用析构函数。它不需要是虚拟的。至少是GCC optimizes this quite well

可悲的是,这意味着您必须使您的储物柜对象可以复制,因为您需要从制造商函数返回它。但旧对象不会尝试解锁两次,因为它的指针在被复制时设置为0,所以它是安全的。

答案 1 :(得分:2)

显然,你无法将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(); }
};

我实际上没有试过这个,语法可能是错的(我不知道如何获取特定函数模板实例的地址),但我认为原则上这应该是可行的。也许有人出现并修复我错了。

我比护卫队更喜欢这个,因为某种原因,我从来没有真正喜欢过。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

我认为jwismar是正确的,你想要的东西是不可能的C ++。但是,使用C ++ 0x可以使用类似的(非直接模拟)构造,使用几个新功能(rvalues / moving和auto variable类型):

#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);
}