【问题标题】:Store member get/set functions for later use存储成员 get/set 函数供以后使用
【发布时间】:2020-08-27 10:31:41
【问题描述】:

我想存储一对成员 get/set 函数以供以后与 T 类型的对象一起使用。

我有一个部分工作的设置,见下文。然而问题仍然存在:

  • 我如何(巧妙地)处理所有可能的变体? member_get 可能是 [返回值或 const value& 甚至 value& | const or non-const] member_set 可以是 [接受 const &, & 或 &&]。当然,“最佳实践”会排除某些组合,但我不能依赖它,因为 member_get 和 member_set 的定义不在我的掌控之中。

  • 如何正确处理可能的 member_set 移动语义?

  • 是否有不同/更好/更简单的通用方法来解决这个问题?

注意事项:

  • 我故意让设置器的确切类型 S 保持打开状态。不确定这是个好主意还是坏主意。
  • 显然我想到了 Lambda,但我看不出它们如何解决这个问题。 Make( get, set ) 的调用者不应该提供 lambda。那就是把问题委托给他!?
  • 任何 std::function 想法都应该因为开销而被排除
template <typename T, typename V, typename G, typename S>
class GetSet
{
public:

    constexpr GetSet( G member_get, S member_set ) : Get( member_get ), Set( member_set )
    { }

    auto GetValue( const T& t ) const 
    {
        return ( t.*Get )( );
    }

    void SetValue( T& t, V&& value ) const
    {
        ( t.*Set )( std::forward<V>( value ) );
    }

private:
    G               Get;
    S               Set;
};

template <typename T, typename ValueType, typename S>
constexpr auto Make( ValueType( T::*member_get )( ) const, S member_set )
{
    using G = ValueType( T::* )( ) const;
    return GetSet<T, ValueType, G, S>( member_get, member_set );
}

【问题讨论】:

    标签: c++ templates member-function-pointers member-functions


    【解决方案1】:

    不知道你为什么需要这个,但最简单的解决方案如下。

    #include <utility>
    
    template <class F> struct ClassType;
    
    template <typename Ret, typename TCls, typename... Args>
    struct ClassType<Ret (TCls::*)(Args...)> {
        using type = TCls;
    };
    
    template <typename Ret, typename TCls>
    struct ClassType<Ret (TCls::*)() const> {
        using type = TCls;
    };
    
    
    template<class TFnGet, class TFnSet>
    class GetSet
    {
       public:
          using TGet = TFnGet;
          using TSet = TFnSet;
       public:
          inline GetSet(TGet fnGet, TSet fnSet)
             : m_fnGet(fnGet), m_fnSet(fnSet)
          {
          }
    
          template<class T>
          inline decltype(auto) GetValue(T&& r) const
          {
             static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnGet>::type>::value, "wrong type of r?");
             return (r.*m_fnGet)();
          }
    
          template<class T, class TValue>
          inline void SetValue(T&& r, TValue&& value)
          {
             static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnSet>::type>::value, "wrong type of r?");
             (r.*m_fnSet)(std::forward<TValue>(value));
          }
    
       private:
          TGet m_fnGet;
          TSet m_fnSet;
    };
    
    
    template<class TGet, class TSet>
    GetSet<TGet, TSet> MakeGetSet(TGet fnGet, TSet fnSet)
    {
       static_assert(std::is_same<typename ClassType<TGet>::type, typename ClassType<TSet>::type>::value, "members of different classes?");
       return GetSet<TGet, TSet>(fnGet, fnSet); 
    }
    

    验证:

    class A
    {
       public:
    
       void Set(int i) {}
       int Get() const { return 0;}
       
       void SetRef(char& ch) {}
       A& GetRef() { return *this;}
       
       void SetRRef(float&& ) {}
       int&& GetRRef() { return 1; }
    
       void SetConstRef(const char& ch) {}
       int GetNotConst() { return 0;}
    };
    
    
    int main(int argc, char* argv[])
    {
       A a;
    
       auto gs = MakeGetSet(&A::Get, &A::Set);
       auto gsRef = MakeGetSet(&A::GetRef, &A::SetRef);
       auto gs2 = MakeGetSet(&A::GetRRef, &A::SetRRef);
       auto gsNonConst = MakeGetSet(&A::GetNotConst, &A::SetConstRef);
    
       int x = gs.GetValue(a);
       gs.SetValue(a, 2);
       const A& ra = a;
       x = gs.GetValue(ra);
    
       A& r = gsRef.GetValue(a);
       char ch =' ';
       gsRef.SetValue(a, ch);
       
       x = gs2.GetValue(a);
       gs2.SetValue(a, 1.1f);
       
       x = gsNonConst.GetValue(a);
       gsNonConst.SetValue(a, ch);
    
       std::cout << "ok\n";
       return 0;
    }
    

    【讨论】:

    • 看不出这比我的代码更简单,也无法解决我提到的不同变体和其他问题。不过,过度使用 ClassType 可能是一种减少变体总数的想法。
    • @non-user38741 我更改了测试代码以显示,这些方法采用所有可能的合理变体作为参数和返回值。
    • 所以魔法发生了,因为ClassType 中的RetArgs... 被推导出为不同的(const 或& 或&&)类型!?很快就会试试这个。提示:总是提示魔法发生的地方(当然,除非你是魔术师)
    • 还是一头雾水。 ClassType 甚至不输入代码,除了错误检查。这怎么可能奏效?编译 - 是的。做正确的事——不确定。由于T&amp;&amp;TValue&amp;&amp; 可能会在GetValueSetValue 中发生魔法
    猜你喜欢
    • 2021-10-11
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多