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
}

1 Answer 1

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
Sign up to request clarification or add additional context in comments.

2 Comments

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
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.