具有混合参数类型的模板重载

时间:2013-04-18 17:56:28

标签: c++ templates

我正在使用C ++编写属性系统(C ++ 11也可以),以便类可以向脚本系统提供数据。我想提供各种“类型”的属性,最简单的属性在内部保持其价值,如下所示:

// Simple Property
template<typename Value>
class Property {
    public:

        Value get() const;

        void set(Value value);

    private:

        Value m_value;
}

对于大多数情况,这没关系,但有时,由于其他原因,该值已计算或必须在属性对象之外。对于这种情况,我想提供这样的模板:

// Complicated Property
template<
    typename Value, 
    typename ValueOwner, 
    Value (ValueOwner::*Getter)(void) const,
    void (ValueOwner::*Setter)(Value)
>
class Property {
  // Stuff for calling the getter & setter
}

有没有办法让这两个模板共存而不重命名其中一个?我试过这个来专门化Getter / Setter模板:

// Complicated Property
// ...

// Simple Property
template<typename Value>
class Property<Value, void, nullptr, nullptr>;

但编译器并不喜欢这样,抱怨creating pointer to member function non-class type "const void"。 Duh,当然void没有成员函数。

然后我尝试用足够的参数声明模板,然后专门化上面的模板,如下所示:

template<typename A, typename B, typename C, typename D>
class Property;

// Simple Property
// ...

// Complicated Property
// ...

但这也不起作用,因为Getter / Setter模板不希望typename作为第三和第四个参数,而是指向成员函数的指针。

接下来尝试:

// Complicated Property
// ...

// Simple Property
template<typename Value>
class Property<
    Value, 
    Property<Value>, 
    &Property<Value>::get, 
    &Property<Value>::set
>;

编译失败,因为编译器现在没有模板参数中的Property<Value>模板。

好吧,也许这一个:

// Complicated Property
// ...

template<typename Value>
struct PropertyDummy {
    Value get() const;
    void set(Value);
}

// Simple Property
template<typename Value>
class Property<
    Value, 
    PropertyDummy<Value>,
    &PropertyDummy<Value>::get, 
    &PropertyDummy<Value>::set
>;

为getter和setter模板参数产生了template argument involves template parameter(s)。他们当然会这样做。

无论如何,将模板重命名为PropertySimpleProperty之类的内容对我而言非常有用。但我很好奇是否有解决方案,以便我可以写两个

Property<int> simpleProperty;

Property<int, MyClass, &MyClass::get, &MyClass::set> complicatedProperty;

让编译器找出我的意思。

2 个答案:

答案 0 :(得分:3)

是否需要在编译时指定getter和setter?您可以使用ValueValueOwner模板参数获取类型:

template<
    typename Value, 
    typename ValueOwner, 
>
class Property {
public:
    typedef Value (ValueOwner::*Getter)(void) const;
    typedef void (ValueOwner::*Setter)(Value);

    Property(Getter getter, Setter setter)
        : getter(getter), setter(setter) {}

    // ...

private:
    Getter getter;
    Setter setter;
};

然后将适当的函数指针传递给构造函数:

Property<int, MyClass> complicatedProperty(&MyClass::get, &MyClass::set);

答案 1 :(得分:3)

  

但我很好奇是否有解决方案以便[...]

是的,有

您可以使用以下方法。首先,在客户端不应该处理的某个命名空间中创建一个虚拟类模板:

namespace detail
{
    template<typename T>
    struct dummy
    {
        T get() const { return T(); }
        void set(T) { }
    };
}

然后,定义以下主模板:

#include <type_traits>

// Simple Property
template<typename Value,
         typename ValueOwner = detail::dummy<Value>,
         Value (ValueOwner::*Getter)(void) const = &ValueOwner::get,
         void (ValueOwner::*Setter)(Value) = &ValueOwner::set,
         bool = std::is_same<ValueOwner, detail::dummy<Value>>::value
         >
class Property {
    public:
        Value get() const;
        void set(Value value);
    private:
        Value m_value;
};

最后一个虚拟模板参数的值取决于dummy<Value>是否作为第二个参数传递(客户端当然不应该使用dummy类模板)。

然后,当最后一个参数为false(意味着为第二个模板参数提供了一些参数)时,您可以专门设置模板:

// Complicated Property
template<
    typename Value,
    typename ValueOwner,
    Value (ValueOwner::*Getter)(void) const,
    void (ValueOwner::*Setter)(Value)
>
class Property<Value, ValueOwner, Getter, Setter, false> 
{
  // Stuff for calling the getter & setter
};

最后,你可以使用你提出的两个声明:

struct MyClass
{
    int get() const { return 42; }
    void set(int) { }
};

int main()
{
    Property<int> simpleProperty;
    Property<int, MyClass, &MyClass::get, &MyClass::set> complicatedProperty;
}

这是live compiling example