Why is there no standardized way to avoid duplication of const method code?

In my experience, a common occurrence is that the same code is used in constant and non-invert versions of a member method. One way to avoid duplication of complex methods is to use const_cast to remove a constant in a non const version, such as Scott Meyers, recommended in Effective C ++ (clause 3). However, this is not beneficial in very short methods that can simply return a pointer - of course, duplication in this case is not so problematic. However, this makes me wonder if there is a reason why there is no keyword or something equivalent to replace casting. I could imagine the following expression:

 autoconst Data* const getData() autoconst; 

Of course, this keyword will not add any functionality that could not be implemented before, but I think it would be nice to have it. As far as I know, the auto keyword likewise does not allow any new constructs, but it is a good simplification in the code - admittedly much more extensive (please correct me if I am wrong).

My question is whether this contradicts some of the rules in the C ++ standard, and if not, is it just not useful enough for implementation.

+7
c ++
source share
2 answers

There is a standardized method, but not many use it:

 class X { T data; public: template<typename autoconst> friend /* or static */ auto get_data(autoconst& that) -> decltype(that.data) { // process that and return that.data } }; 

It is called as get_data(x) , not x.get_data() , but one implementation serves both to use const and not const , without casting or other methods without types.

You can also have member functions to enable member invocation syntax. This will require options const and non const , but not duplication of the process that.data , since both can delegate the template to a friend.

A more complete example:

 template<typename T> class HyperMatrix { int rows, cols, planes; T* data; /* they get initialized somehow */ public: template<typename ThisType> friend /* or static */ auto at(ThisType& that, int const r, int const c, int const p) -> decltype(*(that.data)) { // argument validation logic not being duplicated if (r < 0 || r >= that.rows) throw index_exception(); if (c < 0 || c >= that.cols) throw index_exception(); if (p < 0 || p >= that.planes) throw index_exception(); // complicated indexing expression, also not duplicated const index = (p * that.rows + r) * that.cols + c; return that.data[index]; } // these enable a more natural syntax than at(hymatrix, 1, 2, 3) T& operator()(int const r, int const c, int const p) { return /* ThisType = HyperMatrix<T> */ at(this, r, c, p); } const T& operator()(int const r, int const c, int const p) { return /* ThisType = const HyperMatrix<T> */ at(this, r, c, p); } }; 

An example without a trivial workaround:

 template<typename T> class BalancedJumpTable { public: template<typename ThisType, typename Functor> friend /* or static */ auto for_each(ThisType& that, Functor f) { // complicated code for walking the tree not duplicated // deep inside loops and recursive calls, we find f(that->something()); // maybe there even some filtering logic which causes only // only certain items to be passed to the callback } template<typename Functor> void for_each(Functor f) { return for_each(this, f); } void for_each(Functor f) const { return for_each(this, f); } }; 
+5
source share

I am not for sale by your need. Actually, I am not for sale that your code is completely correct if you find yourself casting const many times.

Note that you can pass const objects to objects that expect const objects. const by parameter is just a contract that the caller will not change the object - it either does or does not. If it changes it, passing the const object is an error (an error in which is discussed, but somewhere there is an error). If he does not change it, you can convey anything.

I'm not sure what else to say, other than try rewriting code with the right contracts, or if that doesn't bother you, just don't use const at all.

-3
source share

All Articles