【问题标题】:Template function taking generic pointer to member function with both const& and by-value implementations模板函数采用指向具有 const& 和按值实现的成员函数的通用指针
【发布时间】:2019-10-11 16:29:16
【问题描述】:

我想要一个模板函数,它接受某个泛型类型实例的一元成员函数指针。 我的问题是我必须同时支持 void(T val) 和 void(const T& val) 成员函数。

我为每种情况编写了一个模板函数,它工作正常,但这会导致代码重复,因为函数逻辑完全相同。 (我在这里找到了完全相似的东西:Function taking both pointer to member-function and pointer to const member-function,但我没有看到明确的解决方案)。

上面提到的泛型类型的一个例子:

using UserAddress = std::string;

class User
{
private:
    int mHeight;
    UserAddress mAddress;
public:
    void SetHeight(int height){mHeight = height;}
    void SetAddress(const UserAddress& address){mAddress = address;}
};

UserAddress 是一些我想通过引用传递的重类型。

我的模板函数:

template <typename TPersistentObject>
class Persistence
{
private:
    std::map<std::string, std::function<void(User*)>> mSetterOfProperty;

    template <typename TPersistentObject, typename TPropertyValue>
    void DefinePropertySettingMethod(const std::string& propertyName,
                                     void (TPersistentObject::*propertySetter)(TPropertyValue), std::function<TPropertyValue(void)> dataReader)
    {
            mSetterOfProperty[propertyName] =
                [propertySetter, columnDataReader](TPersistentObject* persistentObject) 
            {
                (persistentObject->*propertySetter)(dataReader());
            };
    }
};

/// Const& implementation leading to code duplication
    template <typename TPersistentObject, typename TPropertyValue>
    void DefinePropertySettingMethod(const std::string& propertyName,
                                     void (TPersistentObject::*propertySetter)(const TPropertyValue&), std::function<TPropertyValue(void)> dataReader)
    {
...
    }
};

有没有办法定义这个函数来支持以下:


int main()
{
    
    auto  intDataReader = []() {
        return 1;
    };
    
    auto  stringDataReader = []() {
        return UserAddress("Next Door");
    };
    
  Persistence p;
  p.DefinePropertySettingMethod<User,int>("Height", &User::SetHeight, intDataReader);
  p.DefinePropertySettingMethod<User,UserAddress>("Address", &User::SetAddress, stringDataReader);
   
}

【问题讨论】:

  • This close approximation 到您的代码仅使用第一个重载进行编译。不清楚为什么你觉得你需要两者。
  • 我没有在函数中包含第三个参数。我很抱歉。我已经修改了代码。现在你应该能够得到错误。我现在也看到了问题(第三个参数类型的 const& 替换),但问题仍然存在。也许是一些特质(我不熟悉特质)。
  • 有两个单独的模板参数代替TPropertyValue - 一个用于setter,一个用于getter。让他们独立推演。
  • 这很好用(我现在什至不需要显式类型声明)。谢谢!。然而,它放松了传递给 setter 函数的类型和从 dataReader 返回的类型之间的类型依赖性。在函数中,我调用 dataReader 并在 setter 中传递它的返回值。如果存在类型不匹配,编译器会抱怨,但为了清楚起见,我希望在函数原型上表达这种依赖关系。
  • 您可以简单地对类型不匹配进行静态断言...

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


【解决方案1】:

感谢Igor Tandetnik 的提示,我设法编译了一个解决方案。 std::enable_if 不是我需要的,因为我不需要停用过载(或者至少我无法使用它找到解决方案)。 std::conditional 成功了。

代码如下:

#include <string>
#include <functional>
#include <map>
#include <string>
#include <type_traits>

using UserAddress = std::string;

class User
{
private:
    int mHeight;
    UserAddress mAddress;
public:
    void SetHeight(int height){mHeight = height;}
    void SetAddress(const UserAddress& address){mAddress = address;}
};

template <typename TPersistentObject>
class Persistence
{
public:
    std::map<std::string, std::function<void(TPersistentObject*)>> mSetterOfProperty;

    template <typename TPropertyValue>    
    void DefinePropertySettingMethod(const std::string& propertyName,
                                     void (TPersistentObject::*propertySetter)(TPropertyValue), 
                                     std::function<
                                             typename std::conditional<!std::is_same<TPropertyValue, typename  std::decay<TPropertyValue>::type>::value,
                                             typename std::decay<TPropertyValue>::type, TPropertyValue>::type
                                     (void)> dataReader)
    {
            mSetterOfProperty[propertyName] =
                [propertySetter, dataReader](TPersistentObject* persistentObject) 
            {
                (persistentObject->*propertySetter)(dataReader());
            };
    }
};


int main()
{

    std::function<int()>  intDataReader = []() {
        return 1;
    };

    std::function<std::string()>  stringDataReader = []() {
        return UserAddress("Next Door");
    };

  Persistence<User> p;
  p.DefinePropertySettingMethod("Height", &User::SetHeight, intDataReader);
  p.DefinePropertySettingMethod("Address", &User::SetAddress, stringDataReader);

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2010-09-13
    • 2011-03-04
    • 2020-09-20
    相关资源
    最近更新 更多