TruLa
TruLa

Reputation: 1147

Store either constant or non constant reference within the same class template

The example below is degenerate because I would like to learn about the concept.

Let's say we would like to have a 1-element view of an array.

My question is how to make it work with both const and non-const objects.

I know why the second block in the code below does not compile but I don't know how to organize the code to serve both cases.

#include <cassert>
#include <array>

template <typename T>
class View {
 private:
  const std::size_t index_;
  T &t_;
  using value_type = typename T::value_type;

 public:
  View(T &t, std::size_t index) : t_{t}, index_{index} {}

  const value_type &value() const { return t_[index_]; }
  value_type &value() { return t_[index_]; }
};

int main() {
  using Array = std::array<int, 2>;

  // The block below works
  {
    Array array{0, 0};
    View<Array> view(array, 0);
    view.value() = 5;
    assert(array[0] == 5);
  }

  // The block below gives a compilation error
  {
    const Array array{5, 5};
    View<Array> view(array, 0);
    assert(view.value() == 5);
  }
}

Upvotes: 2

Views: 74

Answers (1)

n314159
n314159

Reputation: 5095

The following works:

#include <cassert>
#include <array>

template <typename T>
class View {
 private:
  using value_type = typename T::value_type;

  T &t_;
  const std::size_t index_;

 public:
  View(T &t, std::size_t index) : t_{t}, index_{index} {}

  const value_type &value() const { return t_[index_]; }

  template<class Arr = T, class = typename std::enable_if<!std::is_const<Arr>::value>::type>
  value_type &value() { return t_[index_]; }
};

int main() {
  using Array = std::array<int, 2>;

  // The block below works
  {
    Array array{0, 0};
    View<Array> view(array, 0);
    view.value() = 5;
    assert(array[0] == 5);
  }

  // The block below gives a compilation error
  {
    const Array array{5, 5};
    View<const Array> view(array, 0);
    assert(view.value() == 5);
  }
}

If you give the View a const Array you also have to specify a const Array as template argument.

But then returning a non-const reference with value() doesn't work anymore, so we disable this function with SFINAE, if the array type is const.

PS: You wouldn't have the last problem if your class named View were indeed what one would expect under a view, i.e. non-modifiying and not even having a method returning a non-const reference.

Upvotes: 5

Related Questions