2

In various situations I have a collection (e.g. vector) of objects that needs to be processed by a number of functions. Some of the functions need to modify the objects while others don't. The objects' classes may inherit from an abstract base class. Hence, I have something like this:

class A
{
public:
    virtual void foo() const = 0;
    virtual void bar() = 0;

    /* ... */
};

void process_1(std::vector<std::reference_wrapper<A>> const &vec);
void process_2(std::vector<std::reference_wrapper<A const>> const &vec);

Obviously (?) I can't pass the same vector of std::reference_wrapper<A>s to both process_1 and process_2. Solutions I've considered so far include:

  • Using a C-style cast or reinterpret_cast on a reference to vec
  • Writing my own reference wrapper that has T& get() and T const & get() const instead of T& get() const
  • Refactoring with e.g. methods that take a wrapper instead of the vector
  • Having copies of the vector with and without const
  • Not using const in reference_wrapper's argument

None of these seems very elegant. Is there something else I could do?

tsnorri
  • 1,966
  • 5
  • 21
  • 29
  • I don't see the point of having a reference_wrapper which reference is const. Thats not for modifying the data in another location, is to share the ownership of the data. And thats what `std::shared_ptr` is designed for and works pretty well. I think `std::reference_wrapper` is designed for non-const references only, that explains why only a `T& get() const` overload is provided. – Manu343726 Jun 22 '14 at 15:02
  • `std::shared_ptr` also has `T* get() const`, so I don't think it would help me in this situation. I probably should have mentioned that `A` is abstract. I wanted some additional safety by using references instead of pointers. – tsnorri Jun 22 '14 at 15:22

2 Answers2

3

Range adapters.

A range adapter takes a range as input (a container is a range, as it has begin and end returning iterators), and returns a range with different properties.

You'd cast your reference wrappers to the const variant when you dereference the iterator.

boost has iterators that will do this for you (transform iterators), and tools to help write conforming iterators, but it can be done from scratch with some work.

A bit of extra work could even keep the typenames sane.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

Even lacking elegance, I would make a reference wrapper:

#include <functional>

template <typename T>
class ReferenceWrapper
{
    public:
    ReferenceWrapper(T& ref)
    :   m_ref(ref)
    {}

    ReferenceWrapper(const std::reference_wrapper<T>& ref)
    :   m_ref(ref)
    {}

    const T& get() const noexcept { return m_ref.get(); }
    T& get()  noexcept { return m_ref.get(); }

    operator const T& () const  noexcept { return m_ref.get(); }
    operator T& ()  noexcept { return m_ref.get(); }

    private:
    std::reference_wrapper<T> m_ref;
};

It is a tiny class modeling the original requirements.