Is overloading operator() for a reconstruction a good practice?

前提是你 提交于 2020-02-24 14:35:29

问题


I was thinking of the following scenario:

class A {
    private:

    std::string id;
    std::array<std::string, 128> data;

    public:

    A(const std::string& id) : id(id) {}
    A(const A& other) : id(other.id), data(other.data) {}
    virtual ~A(){}

    //to override the intern data
    A& operator=(const A& other) {

        this->data = other.data;

        return *this;
    }

    //to override the whole element
    A& operator()(const A& other) {

        this->id = other.id;
        this->data = other.data;

        return *this;
    }
};

As you can see, my idea was to use operator= to override the internal data and operator() to override the whole element. I was inspired by the constructor which would allow A a(anOtherA); to construct the element and I would like to override this for a re-construction. Now I don't now if this would be smart overloading this because it's actually the function call operator.


回答1:


Is overloading operator() for a reconstruction a good practice?

In short no, that isn't good practice. Such just obfuscates what is done under the hood.

Providing a setter for data and use the code you provided in your overloaded operator() for the implementation of the assignment operator=() would provide the clearer and naturally expected semantics:

class A {
    private:

    std::string id;
    std::array<std::string, 128> data;

    public:

    A(const std::string& id) : id(id) {}
    A(const A& other) : id(other.id), data(other.data) {}
    ~A(){}

    //to override the intern data
    A& operator=(const A& other) {
        id = other.id;
        data = other.data;

        return *this;
    }

    //to override the intern data
    void setData(const A& other) {
         data = other.data;
    }
    void setData(const std::array<std::string, 128>& data_) {
         data = data_;
    }
};

The semantics of the operator() isn't that clearly defined (vs the operator=()) beyond you can make a call of your class looking like a "normal" function call (which is mostly useful with templates taking your type as a parameter).
But I'd expect it more to do some action instead of changing the internal state of the class.


Regarding the style, instead of the set / get prefixes for getter/setter functions I prefer what's done in the c++ standard library, (like e.g. with the std::ios_base::flags() property):

class A {
private:
    // ...
    std::array<std::string, 128> data_;
public:
    const std::array<std::string, 128>& data() const {
         return data_;
    }
    void data(const std::array<std::string, 128>& data) {
         data_ = data;

    }
    // ...
};



回答2:


great answer from πάντα ῥεῖ so please upvote that answer, not this one.

As you write, and more importantly, read more c++ you will come to appreciate people who name methods and functions with natural, meaningful names.

For most of us, if we see code like this:

X x;
Y y;

x(y);

We would think, before even looking at the declarations of X and Y, that X is some kind of function object (i.e. it does something) and Y is some kind of data or state object - it likes having things done to it, or it supplies data or services.

As a side note, a Haskell programmer would naturally assume that Y is also a function, but that's another story.

If your implementation of X::operator()(Y) does not "do X-type stuff with or to a Y" then it is probably inappropriately named.

If Y actually represents new state for X, and X intends to 'reset' itself using the data in Y, then the method should probably be called... reset:

X x;
Y y;

x.reset(y);  //ok, this is telling a better story

With reasonable names we can tell a narrative with our code:

void processResults(XFactory& factory, std::istream& is) {
    while(is) {
        auto x = X::createFrom(factory);
        x.collectNResults(is, 10);
        auto a = x.takeAverage();
        storeAverage(a);
        x.reset(y);
    }
}

Now even without looking up the definitions of the various classes I can get a sense of the general narrative. It's easier on the eye and I'm going to be able to hone in on the bits I need to see much more quickly than:

void processResults(XFactory& factory, std::istream& is) {
    while(is) {
        auto x = X(factory);
        x(is, 10);
        auto a = x();
        x(averageStore);
        x(y);
    }
}

Which is what I'd have if I wrote every operation on an X in terms of a call operator which, much like corporate tax avoidance, is actually perfectly legal, but nevertheless happens to upset other people because they end up paying the price for your selfishness.



来源:https://stackoverflow.com/questions/41149115/is-overloading-operator-for-a-reconstruction-a-good-practice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!