5

I have two classes, say 'Base' and 'Derived', where Derived class inherits Base class.

Then a container of references to Derived class instances (std::vector< std::reference_wrapper< Derived > > myContainer ).

And finally, I have a function that takes std::vector< std::reference_wrapper< Base > > as argument.

If I pass the container(myContainer) to the function, it doesn't compile:

enter image description here

If I change my container to hold references to Base, everything works fine and since it is a reference_wrapper, I believe, I will still have the polymorphic behaviour that I need. But this feels unclean as I am sure that my container will not hold anything other than Derived instances.

At the same time, the function that takes in the vector is supposed to work on both Derived and Base classes.

Minimal code:

#include <vector>
#include <functional>

class Base
{ /*some virtual stuff*/ };

class Derived : public Base
{};

void Fun( std::vector< std::reference_wrapper< Base > > const & container )
{}

int main()
{
    std::vector< std::reference_wrapper< Derived > > myContainer;
    Fun( myContainer ); // Error: no viable conversion
    return 0;
}

Live code: https://godbolt.org/z/SX5Gag

What is the best way to ask the Function to treat the container of Derived references as a vector of Base references?

sajas
  • 1,599
  • 1
  • 17
  • 39
  • 1
    Typical solutions for this are: (a) _templating_ your function `Fun`; or (b) copying the "derived" vector into a "base" vector. – paddy Oct 11 '19 at 13:35

1 Answers1

4

That's just not possible:

std::vector< std::reference_wrapper< Derived > > d;
std::vector< std::reference_wrapper< Base > >& b = d;

What could happen, if this was legal?

SomeOtherDerivedClass o;
b.push_back(o); // sure, b is vector of Base, so legal

But b actually just is a reference to d, so you just managed to place a different, illegal type into d.

So even if Base is a base class of Derived, the same doesn't apply for the corresponding containers, be it std::vector or any other one.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • Oops. That was so basic. Thanks – sajas Oct 11 '19 at 13:31
  • I guess the same rule applies for this case: https://godbolt.org/z/iyTzfN. right? – sajas Oct 11 '19 at 13:34
  • 1
    @sajas Well, that's more complicated. Truth is that actually, any two template instantiations are unrelated classes, whether the two underlying types are related or not. However, `std::reference_wrapper` has a non-explicit cast-operator to reference. That's why your chosen compiler accepts if you change to `... refBase(refDer);`! With same argument, I would have expected the compiler to accept your example, too, failing so surprises me pretty much. If you select newest clang (or newest GCC), code is accepted (even if you explicitly select c++11, so apparently not related to standard version). – Aconcagua Oct 11 '19 at 15:22
  • 1
    Be aware, though that this assignment or copy construction *only* is possible due to the cast operator, otherwise, *both* would fail even with newest clang/GCC. – Aconcagua Oct 11 '19 at 15:26
  • So, the code compiles(after making the the change you suggested) because reference_wrapper has a cast operator that returns the reference to derived class, which is used for the construction of the second wrapper. Without the cast this won't happen. And the code compiles, NOT because the wrappers are convertible. Right? – sajas Oct 11 '19 at 15:35