我有以下用于锁定对象的类:

#include <memory>

template <class Type, class Mutex>
class LockableObject {
 public:
  class UnlockedObject {
   public:
    UnlockedObject(Mutex &mutex, Type &object)
        : mutex_(mutex), object_(object) {}
    UnlockedObject(UnlockedObject &&other) = default;

    // No copying allowed
    UnlockedObject(const UnlockedObject &) = delete;
    UnlockedObject &operator=(const UnlockedObject &) = delete;

    ~UnlockedObject() { mutex_.unlock(); }

    Type *operator->() { return &object_; }   // Version 1
    // Type &operator->() { return object_; } // Version 2


   private:
    Mutex &mutex_;
    Type &object_;
  };

  template <typename... Args>
  LockableObject(Args &&... args) : object_(std::forward<Args>(args)...) {}

  UnlockedObject Lock() {
    mutex_.lock();
    return UnlockedObject(mutex_, object_);
  }

 private:
  Mutex mutex_;
  Type object_;
};

我想按以下方式使用它来锁定和解锁对共享库的访问。第二个示例利用->运算符多次应用自身的能力:
  // Example 1
  {
    LockableObject<std::string, std::mutex> locked_string;
    auto unlocked_string = locked_string.Lock();
    // This is what I want:
    unlocked_string->size(); // works for version 1, breaks for version 2
  }

  // Example 2
  {
    LockableObject<std::unique_ptr<std::string>, std::mutex> locked_string(std::unique_ptr<std::string>(new std::string()));
    auto unlocked_string = locked_string.Lock();
    // This is what I want:
    unlocked_string->size(); // works for version 2, breaks for Version 1

    // Workaround
    unlocked_string->get()->size(); // works for version 1, but is not nice
  }

是否可以将类更改为两个示例都使用unlocked_string->size()而不是->get()的变通方法?可能是通过使用模板特化或类似方法?

最佳答案

编写LockablePtrLockableValue类型。

LockableObject根据传入的类型有条件地选择上述哪一个。使用某种SFINAE或特征或类似方法检测智能指针。

该选择可以通过using别名,也可以通过与using的继承来获取父构造函数。

namespace details {
  template<template<class...>class, class, class...>
  struct can_apply : std::false_type {};
  template<class...>struct voider{using type=void;};
  template<class...Ts>using void_t=typename voider<Ts...>::type;
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, void_t<Z<Ts...>>, Ts...> : std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;

template<class T>
using star_r = decltype( *std::declval<T>() );
template<class T>
using is_ptr_like = can_apply< star_r, T >;
is_ptr_like是可以一元取消引用的事物的特征。

假设您同时编写了LockablePtr<T,M>LockableValue<T,M>
template<class T, class M>
using Lockable =
  typename std::conditional< is_ptr_like<T&>::value,
    LockablePtr<T, M>,
    LockableValue<T, M>
  >::type;

template<class T, class M>
struct LockableObject:Lockable<T,M> {
  using Lockable<T,M>::Lockable;
};

并做了。

顺便说一句,您选择存储
Type&

好像是坏事。

10-04 13:00