2017-06-09 13 views
2

Iは、オブジェクトをロックするための次のクラスがあります。私は次のようにロックし、共有オブジェクトへのアクセスのロックを解除するためにそれを使用したい2つの異なるバージョンの矢印演算子がありますか?

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

クラスは何とか両方の例は、->get()と回避策の代わりにunlocked_string->size()を使っ持つように変更することができますか?おそらく、テンプレートの特殊化などを使用してですか?

+1

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 {}; 

そして、条件付きで、このような関数の2つのバージョンを有効または無効に:私は、矢印演算子の存在を検出するために、次を使用して終了しましたか? – Yakk

+0

'unlocked_string() - > size()'のようなものを得るために 'operator()'をオーバーロードしてください。問題は本当にあなたの 'get()'が 'std :: unique_ptr'から来ていることです。 'get()'を取り除きたい場合は、 'unique_pointer'のテンプレートを特化しなければなりません – subzero

+0

@subzeroは' unique_ptr'に特化しています。バージョン2の '型'にオーバーロードされた '演算子 - > 'と他の' Type'sのバージョン1はうまくいくでしょう...これを行う方法の詳細は? –

答えて

0

ヒントと回答ありがとうございます。 `LockablePtr`タイプを考えてみましょう

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 
1

書き込みLockablePtrLockableValueタイプです。

LockableObjectは、渡された型に基づいて上記のいずれかを条件付きで選択します。スマートポインタを何らかの種類のSFINAEまたは特質やsomesuchを使用して検出します。

この選択肢は、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& 

は悪いもののように思えます。

+0

詳細な回答をいただきありがとうございました。結局、私は非常に似たようなことをしました。あなたはなぜタイプ&を保存するのが悪い考えであると思いますか? UnlockedObjectクラスを介してobject_にアクセスする必要があります。 –

+1

@jan 2番目の読み取りでは大丈夫です。ロックされたオブジェクトではなく、ロックされたオブジェクトであると思った。 – Yakk

関連する問題