inheritance - Add union of subclasses to vector of base class in C++ - Stack Overflow

admin2025-04-08  0

I have a base class A, and two subclasses B and C which inherit from A. I would like to be able to store a union of B and C in an std::vector<A*>, but it seems that it may not be possible. Here's a minimal reproducible example of the situation that creates an error:

#include <vector>

struct A {};

struct B : public A {};
struct C : public A {};

union D
{
    B* b;
    C* c;
};

int main(int argc, char** argv)
{
    std::vector<A*> v;

    v.push_back(D());
    v.push_back(new D());

    return 0;
}

In the above example, both push_back lines throw an error. I have also tried removing the * from B and C in the union, and the same lines still throw an error. Am I trying to do something impossible or is there a way to get around this?

I have a base class A, and two subclasses B and C which inherit from A. I would like to be able to store a union of B and C in an std::vector<A*>, but it seems that it may not be possible. Here's a minimal reproducible example of the situation that creates an error:

#include <vector>

struct A {};

struct B : public A {};
struct C : public A {};

union D
{
    B* b;
    C* c;
};

int main(int argc, char** argv)
{
    std::vector<A*> v;

    v.push_back(D());
    v.push_back(new D());

    return 0;
}

In the above example, both push_back lines throw an error. I have also tried removing the * from B and C in the union, and the same lines still throw an error. Am I trying to do something impossible or is there a way to get around this?

Share Improve this question asked Mar 26 at 21:46 ERSUCCERSUCC 708 bronze badges 6
  • 2 D is not derived from A, so there is no implicit pointer conversion, nor any other way to pretend that a D object is an A object. The most natural thing would be to use std::vector<D>. – Pete Becker Commented Mar 26 at 22:06
  • 2 std::vector<std::variant<A, B, C, D>> v; No need for pointers. – Eljay Commented Mar 26 at 22:12
  • 3 Why do you want to do this? You have no way to determine if the D holds a B* or a C*, so how do you plan to use it? If A is polymorphic then just put B*s or C*s in your std::vector<A*>. If not, then you need some sort of tagged union like std::variant – Miles Budnek Commented Mar 26 at 22:27
  • It seems like I may have made a poor design decision that led to this point. In my actual code (not this example) type D is supposed to be able to return an instance of B if asked for an instance of B, or an instance of C if asked for an instance of C ("asked" meaning some other function is traversing the vector and getting objects from it). I thought I might be able to use a union for this purpose, but maybe it isn't the right data structure. – ERSUCC Commented Mar 26 at 22:31
  • 1 In modern C++ you rarely need to use a union directly except when you are doing low-level (close to bare-metal) programming. – Weijun Zhou Commented Mar 27 at 6:07
 |  Show 1 more comment

1 Answer 1

Reset to default 3

In my actual code (not this example) type D is supposed to be able to return an instance of B if asked for an instance of B, or an instance of C if asked for an instance of C ("asked" meaning some other function is traversing the vector and getting objects from it).

A union is not the right choice for this design. D does not derive from A, so you can't store D objects into a vector of A* pointers.

Besides, the only reason to store A* pointers in the vector in the first place is if you want to store individual B and C objects together. In which case, you can use dynamic_cast to find them, eg:

#include <vector>

struct A { virtual ~A() = default; };

struct B : public A {};
struct C : public A {};

int main(int argc, char** argv)
{
    std::vector<A*> v;

    v.push_back(new B);
    v.push_back(new C);

    for(A *a : v)
    {
        if (B *b = dynamic_cast<B*>(a))
        {
            // use b as needed...
        }
        else if (C *c = dynamic_cast<C*>(a))
        {
            // use c as needed...
        }
    }

    for(A *a : v)
    {
        delete a;
    }

    return 0;
}

Or, you can use std::variant instead, and not use A* pointers at all, eg:

#include <vector>
#include <variant>
#include <type_traits>

struct A { virtual ~A() = default; };

struct B : public A {};
struct C : public A {};

int main(int argc, char** argv)
{
    std::vector<std::variant<B,C>> v;

    v.emplace_back(B{});
    v.emplace_back(C{});

    for(auto &var : v)
    {
        std::visit([](auto&& arg)
        {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, B>)
            {
                // use arg as B as needed...
            }
            else if constexpr (std::is_same_v<T, C>)
            {
                // use arg as C as needed...
            }
        }, var);
    }

    return 0;
}

Alternatively:

#include <vector>
#include <variant>

template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };

template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

struct A { virtual ~A() = default; };

struct B : public A {};
struct C : public A {};

int main(int argc, char** argv)
{
    std::vector<std::variant<B,C>> v;

    v.emplace_back(B{});
    v.emplace_back(C{});

    for(auto &var : v)
    {
        std::visit(overloaded{
            [](B& arg) {
                // use arg as needed...
            },
            [](C& arg) {
                // use arg as needed...
            }
        }, var);
    }

    return 0;
}
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1744124361a232395.html

最新回复(0)