我有以下用于锁定对象的类:
#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()
的变通方法吗?可能通过使用模板专业化或类似的东西?
答案 0 :(得分:1)
撰写LockablePtr
和LockableValue
类型。
让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&
看起来很糟糕。
答案 1 :(得分:0)
感谢您的提示和答案。我最终使用以下内容来检测箭头操作符的存在:
template <class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void`
template <class T> using type_sink_t = typename type_sink<T>::type;
template <class T, class = void> struct has_arrow : std::false_type {};
template <class T> struct has_arrow<T, type_sink_t<decltype(std::declval<T>().operator->())> > : std::true_type {};
然后有条件地启用或禁用这两个版本的函数:
template <class CopyType = Type>
typename std::enable_if<!has_arrow<CopyType>::value, CopyType *>::type
operator->() { return &object_; } // Version 1
template <class CopyType = Type>
typename std::enable_if<has_arrow<CopyType>::value, CopyType &>::type
operator->() { return object_; } // Version 2