Вывод типа шаблона с не копируемым классом - PullRequest
2 голосов
/ 16 мая 2010

Предположим, у меня есть класс автоматической блокировки, который выглядит примерно так:

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 ]

5 голосов
/ 16 мая 2010

Да, вы можете использовать технику прицела

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 довольно хорошо это оптимизирует .

К сожалению, это означает, что вы должны сделать свой объект-локер копируемым, поскольку вам нужно вернуть его из функции создателя. Но старый объект не будет пытаться разблокировать дважды, потому что его указатель при копировании устанавливается в 0, так что это безопасно.

2 голосов
/ 16 мая 2010

Очевидно, что вы не можете сойти с 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(); }
};

На самом деле я не пробовал этого, и синтаксис может быть неправильным (я не уверен, как получить адрес конкретного экземпляра шаблона функции), но я думаю, что это должно быть выполнимо в принципе. Может быть, кто-то приходит и исправляет то, что я ошибся.

Мне это нравится намного больше, чем охрана прицела, которая по какой-то причине мне вообще никогда не нравилась.

1 голос
/ 16 мая 2010

Я думаю, что jwismar верен, а то, что вы хотите, невозможно с C ++. Однако подобная конструкция (не прямой аналог) возможна в C ++ 0x с использованием нескольких новых функций (rvalues ​​/ moving и auto variable type):

#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);
}
1 голос
/ 16 мая 2010

autolocker - это шаблон класса, а не класс. Ваше «это было бы идеально» показывает то, что не имеет смысла в C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...