2

我正在使用 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". 呃,当然 void 没有成员函数。

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

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

// Simple Property
// ...

// Complicated Property
// ...

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

下次尝试:

// 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
>;

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

无论如何,将模板重命名为对我有用的东西PropertySimpleProperty足够了。但我很好奇是否有解决方案,以便我可以同时编写

Property<int> simpleProperty;

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

并让编译器弄清楚我的意思是哪个模板。

4

2 回答 2

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);
于 2013-04-18T18:01:55.727 回答
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;
}

这是一个实时编译示例

于 2013-04-18T18:11:20.303 回答