Pergunta

I want to do method chaining on class Point below.

#include <iostream>

class Point {
  public:
    Point(int x, int y): _x(x), _y(y) {}

    Point& moveX(int x);
    Point& moveY(int y);
    Point& print() const;

  ...
};

...

Point& Point::print() const {
  std::cout << "(" << _x << "," << _y << ")" << std::endl;
  return *this;  // Compile fails
}

I think it makes sense to mark print() as const member function because it just print the internal members. However, I want to do method chaining among both non-const and const function like below.

int main() {
  Point p(1,1);
  p.moveX(10).print().moveY(11); // method chaining
}

So I have to return this as non-const but it fails the compilation because, in my understanding, the members are marked const including this in const member function.

Is there a way to do method chaining in this situation?

Foi útil?

Solução 4

The reason why this fails is that inside a const member function, this is really a const Point*, not a Point*. Thus you are trying to initialize a non-const reference from a const pointer. It's not that the compiler isn't believing you, you're just asking for two incompatible things at one time.

This is one of the very few valid uses of const_cast, in my opinion. Normally, using const_cast is almost always a sign of a design error, or worse a programming error.
Here, the function is really const and should be const, but there is no reason why you shouldn't be able to chain something non-const afterwards, so it's arguably legitimate to do such a thing.

Do note, however, although the function is strictly const (in respect to the object, not so much in its use of IO functions!), one thing you should consider is that in some (rare) cases, it may result in code that doesn't do what you want. The compiler is allowed to cache the result of a const function and omit another call to the same const function (since you promised that it won't change anything). Therefore, it is allowable to optimize some_point.Print().Print(); into some_point.Print(). This is probably not a problem for you (why would you want to print the same values twice), just something to be generally aware of.

Outras dicas

You can provide two member functions, one const and one non-const. The const one will be called on a Point const, the non-const one on a Point.

class Point {
public:
    Point(int x, int y): _x(x), _y(y) {}

    Point& moveX(int x);
    Point& moveY(int y);
    Point& print();
    Point const& print() const;

    ...
};

As a side node, it’s better to overload std::ostream& operator<<(std::ostream&, Point const&), so you can use it with any output stream, not just std::cout:

class Point {
    ...
private:
    friend std::ostream& operator<<(std::ostream& stream, Point const& point) {
        stream << "(" << point._x << "," << point._y << ")";
        return stream;
    }
}

IMO you don't understand correctly what const methods can do. If const method return non-constant referance to your object, it's not constant method.

So in your situation you can simply return nothing from print method and use it at the end of the chaining like p.moveX(10).moveY(11).print();.

UPD. Or you can return const Point& if there is possibility that you will add some new const methods to your class.

You can use a const_cast when returning (i.e. return const_cast<Point&>(*this)) - this will make sure that your method can't modify the objects, however the callers will be able to modify it.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top