3

I would like to have a wrapper class that keeps and returns a pointer to some element of a wrapped container. It looks like:

template <typename T>
class VectorWrapper
{
public:
  VectorWrapper(vector<T>& container) {
    m_pointer = &container[0];
  }

  T* GetPointer() { return m_pointer; }

private:
  T* m_pointer;
};

The problem is the input container can be a const type sometimes. In this case, is there an elegant way to avoid another implementation of VectorWrapper that rather returns const T*?

My approach (without luck) was the following:

template <typename T>
VectorWrapper<T> make_vector_wrapper(vector<T>& container) {
  return VectorWrapper<T>(container);
}

template <typename T>
VectorWrapper<T> make_vector_wrapper(const vector<T>& container) {
  // I'm stuck here. return VectorWrapper<const T>(container); doesn't work.
}

void Foo(const vector<int>& const_container) {
  vector<int> mutable_container(10);

  auto v1 = make_vector_wrapper(mutable_container);
  *(v1.GetPointer()) = 1;     // Ok

  auto v2 = make_vector_wrapper(const_container);
  int x = *(v2.GetPointer()); // Ok
  *(v2.GetPointer()) = 1;     // Would like compile error
}
Jonas
  • 6,915
  • 8
  • 35
  • 53
D. Fisher
  • 223
  • 2
  • 12

1 Answers1

3

You could template on the container and not the element, since it is the container that is const. Here is a quick mock-up (which does not work if the vector is relocated):

#include <iostream>
#include <vector>

template <typename T>
class VectorWrapper
{
    using value_type = std::remove_reference_t<decltype(((T*)nullptr)->at(0))>;

public:
  VectorWrapper(T& container) {
    m_pointer = &container[0];
  }

  value_type* GetPointer() { return m_pointer; }

private:
  value_type* m_pointer;
};

template <typename T>
VectorWrapper<T> make_vector_wrapper(T& container) {
  return VectorWrapper<T>(container);
}

int main() {
  std::vector<int> mutable_vector(10);
  auto v1 = make_vector_wrapper(mutable_vector);
  *(v1.GetPointer()) = 1;     // Ok

  const std::vector<int> const_vector(10);
  auto v2 = make_vector_wrapper(const_vector);
//  *(v2.GetPointer()) = 1;     // error
}

Edit:

Here is a simpler solution (C++14 for auto return type), that also handles relocation of the underlying vectors data.

template <typename T>
class VectorWrapper
{
  T& m_container;
public:
  VectorWrapper(T& container) : m_container(container) {}

  auto GetPointer() { return m_container.data(); }
};

Update:

It will also work for other container classes such as std::string and std::array

  std::string mutable_string;
  auto s1 = make_vector_wrapper(mutable_string);
//  *(s1.GetPointer()) = 1;     // error, sd::string::data returns char const *

  std::array<int, 4> mutable_array;
  auto a1 = make_vector_wrapper(mutable_array);
  *(a1.GetPointer()) = 1;     // Ok

  const std::array<int, 4> const_array = {1,2,3,4};
  auto a2 = make_vector_wrapper(const_array);
//  *(a2.GetPointer()) = 1;     // error
Jonas
  • 6,915
  • 8
  • 35
  • 53
  • I think your first solution is pretty dangerous (not sure about the c++14 solution, as I don't know much about auto return type). You better use a `shared_ptr` because if the referenced container in the copy-constructor leaves the scope in which it was originally created you run into desaster if you try to dereference your `m_pointer` – FloHe Feb 26 '17 at 13:47
  • Yes, the first solution is more or less the working version of OP's suggestion. You've got a point, I'll check it out, and get back. – Jonas Feb 26 '17 at 14:05