2013-12-19

Avoiding code duplication between const and non-const methods

Sometimes const and non-const methods in C++ classes share the same name, body, and return type (except for the const keyword), thus causing code duplication.

The problem is described in this StackOverflow topic, and somewhere else on the Internet. The obvious solution is to refactor the code in order to avoid duplication while preserving const-correctness and semantics, but sometimes this is not possible or not convenient. A popular (and effective) solution requires a const_cast and a static_cast. In this post, I suggest an alternative (and I think safer) solution.

Here is the scenario:

class C {

   std::vector<T> v;

public:

   // constructor and other members

   const T& m(size_t i) const {
      // long and complex access logic...
      return v[i];
   }

   T& m(size_t i) {
      // long and complex access logic (again)...
      return v[i];
   }

};

And the popular solution:

class C {

   std::vector<T> v;

public:

   const T& m(size_t i) const {
      // long and complex access logic (only once)...
      return v[i];
   }

   T& m(size_t i) {
      return const_cast<T&>(static_cast<const C&>(*this).m(i));
   }

};

This will work as expected, and one “ugly” line can save you from writing tens of duplicate lines of code. However, even if in this simple case (retrieving an element of a vector) it is 100% safe, it may be unsafe if the returned reference cannot be just removed the const qualifier. In that case, the compiler won’t issue any error or warning.

I propose this alternative solution:

class C {

   std::vector<T> v;

   template<typename Instance>
   static auto m(Instance& instance, size_t i) -> decltype(instance.m(i)) {
      // long and complex access logic (only once)...
      return instance.v[i];
   }

public:

   const T& m(size_t i) const {
      return m(*this, i);
   }

   T& m(size_t i) {
      return m(*this, i);
   }

};

It is equal in ugliness and requires a few more lines of code, but is always safe.

Tags: cpp, cpp11
comments powered by Disqus
Fork me on GitHub