0

Coming from the Java world, I'm trying to accomplish something similar to

import java.util.List;

class BaseClass {
    protected List<? extends BaseClass> members;
}

class ClassA extends BaseClass {
    public ClassA(List<ClassA> args) {   // I want to have only ClassA objects here
        this.members = args;
    }
}

Now in C++, the following doesn't compile because of the mismatch

#include <vector>
#include <memory>

class BaseClass {
protected:
    std::vector<std::shared_ptr<BaseClass>> children;
};

class ClassA : public BaseClass {
    explicit ClassA(const std::vector<std::shared_ptr<ClassA>>& args) {
        this->children = args;  // error here
    }
};

The compilation error is

error: no match for ‘operator=’ (operand types are ‘std::vector<BaseClass>’ and ‘const std::vector<ClassA>’)
   11 |         this->children = args;
      |                          ^~~~

What would be the most idiomatic/cleanest solution? I'm using C++20.

John Doe
  • 800
  • 6
  • 9
  • 1
    `std::shared_ptr` and `std::shared_ptr` as well as `std::vector>` and `std::vector>` are _unrelated_ classes even if `class ClassA: public BaseClass`. You could try to initialize `children` with `children(args.begin(), args.end())`. The constructor / assignment of `std::shared_ptr` should accept `std::shared_ptr` (similar like `ClassA*` can initialize / be assigned to `BaseClass*`). – Scheff's Cat Apr 20 '23 at 15:03
  • 2
    while a `ClassA` is-a `BaseClass`, `vector` and `vector` are not related whether references are involved or not. It's going to have to be `vector` of `BaseClass` references all the way down, I'm afraid. – user4581301 Apr 20 '23 at 15:04
  • 1
    @Scheff'sCat `std::shared_ptr` and `std::shared_ptr` *are* related, there's an implicit conversion from the latter to the former. – Caleth Apr 20 '23 at 15:14
  • 1
    If it weren't closed already, would vote to close for this: https://stackoverflow.com/questions/76004849/cast-vectorint-to-vectorunsigned-int/76005027#76005027 – Red.Wave Apr 20 '23 at 19:45

1 Answers1

3

There is no way in C++ to cast a vector of Ts into a vector of Us, even if U inherits from T. As such you need to insert args one by one; however, the implementation of shared_ptr makes it such that you do not need to explicitly cast. (If you ever do need to explicitly cast you can use std::static_pointer_cast)

#include <vector>
#include <memory>

class BaseClass {
protected:
    std::vector<std::shared_ptr<BaseClass>> children;
};

class ClassA : public BaseClass {
public:
    explicit ClassA(const std::vector<std::shared_ptr<ClassA>>& args = {}) {
        children.reserve(args.size());
        for (auto arg : args) {
            children.push_back(arg);
        }
    }
};
jwezorek
  • 8,592
  • 1
  • 29
  • 46