Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have a template Property which wraps a data and provides other services. This template is specialized for basic data (float, int_32, bool...) and there exists another specilization for vectors of basic types. Something like:

template<typename T>
Property : public PropertyBase
{
public:
    // Base types specilization.
    void operator=(T const & data);
}

template<typename T>
Property<std::vector<T> > : public PropertyBase
{
public:
    // Specilization for vectors of base types.
    T const & operator[](std::size_t i) const;
}

On the specilization for basic data I have overloaded operator= so that a callback is called after the data was changed. On the specilization for vectors I have overloaded operator[] to get indexed access to the ith element of the vector.

Now I would like to get a similar behavior that I have for operator= on basic types but with my specialization for vectors. I am stuck though. What I would like to do is something like:

Property<std::vector<float> > myProp;
myProp[5] = 5.21f;

and expect the callback to be called after the assignement happened.

The problem is that myProp[5] returns a float, and the callback mechanism is not defined for floats but Property.

Of course I can write an indexed setter function such as

template<typename T>
Property<std::vector<T> > : public PropertyBase
{
public:
    // Specilization for vectors of base types.
    T const & operator[](std::size_t i) const;

    void set(int i, T const & newValue);
}

But with the setter the syntax is not as clean as I would expect.

I can also call the callback in operator[] but then the callback is called before the assigment, not after.

Anyone has an idea to get this clean syntax while still being able to call my callback after the assigment?

Thanks!

PS: note that my Property templates are more complex than what is written and do check whether T is a base type or not (which is not done here) but this is not relevant for my question.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
776 views
Welcome To Ask or Share your Answers For Others

1 Answer

You'll need to return a proxy from your operator[]() which [implicitly] converts to your type T and has an assignment operator intercepting the assignment. Here is a sketch:

template <typename T>
class proxy {
    Property<std::vector<T>>* property;
    std::size_t               index;
public:
    proxy(Property<std::vector<T>>* property, std::size_t index)
        : property(property)
        , index(index) {
    }
    operator T const&() const { return property->get(index); }
    T const& operator= (T const& other) {
        return this->property->set(index);
    }
};

Your operator[]() would then return such a proxy:

 template <typename T>
 proxy<T> Property<std::vector<T>>::operator[](std::size_t index) {
     return proxy<T>(this, index);
 }

The actual interception of the reading and writing of the fields would happen in the corresponding set() and get() members. These can be private if proxy<T> is made friend of the class. The main issue with using a proxy class like this is that it doesn't allow the use of member functions, e.g.:

Property<std::vector<std::string>> p;
p[0].c_str();

Since p[0] happens to be a proxy<T> it doesn't have a .c_str() function. Since you stated that you are only supporting certain basic types you could support the suitable operations on the proxy. The other issue with using a proxy type is that it takes out one of the conversions and, strictly speaking, an iterator returning proxies rather than references isn't a proper iterator (although I seem to recall that the iterator requirements were relaxed in C++11).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...