Reputation: 69
So, I have a generic operator. In this specific case its generic template T
is supposed to be a pointer to an abstract base class. The point is, when I create T *t
and try to return *t
I get an error saying T
can't be instantiated because it's an abstract class. Even though *t
will always be an object of derived classes which aren't abstract. It doesn't accept it, because T is after all abstract type. Basically, what I want to achieve is to get a reference to the *t
returned from the operator
to main()
and then assign to it objects of derived classes in main()
.
I understand that there are questions about slicing and polymorphism. But I am trying to understand how can I achieve it in different ways. It's a completely different question which deals with the following specific code.
Code is something like this:
Template <class T>
class myArr{
T & operator [] (int index){
T*t;
Arr[0] = *t;
Return arr[0[;
}}
Main(){
Base * b=new derived;
myArr<Base>[17]=*b;
// Point is, I can make it of type pointers and maybe it can
//work but I prefer assigning *b to myArr because I want
to //assign objects themselves
}
My question is, how can I return the object in itself from the operator
? I want to return something to which I can assign a derived object . I am trying to do it with pointer of type abstract base, so as to achieve polymorphism so it can contain any object of derived classes.
Upvotes: 0
Views: 842
Reputation: 117318
I tried making something out of the code you showed by using std::unique_ptr<Base>
in your array. It shows how you can replace objects in the array (base_arr[0] = ...
) and how to update existing objects in the array (*base_arr[0] = ...
). I added two derived classes carrying different types (a std::string
and an int
) and a lot of debug prints so it's easier to follow what's happening when you run it.
#include <iostream>
#include <vector>
#include <memory>
#include <string>
// Your container wrapper with some functions
template<class T>
class myArr {
public:
using type = T;
template<class... Args>
decltype(auto) emplace_back(Args&&... args) {
return arr.emplace_back(std::forward<Args>(args)...);
}
T& operator[](std::size_t index) { return arr[index]; }
auto begin() const { return arr.begin(); }
auto end() const { return arr.end(); }
T extract_front() {
T retval(std::move(arr.front()));
arr.erase(arr.begin());
return retval;
}
private:
std::vector<T> arr{};
};
//----------------------------------------------------------------------------------
struct Base {
Base() = default;
Base(const Base&) = default;
Base(Base&&) = default;
virtual ~Base() = 0;
virtual Base& operator=(const Base&) = 0;
virtual Base& operator=(Base&&) = 0;
virtual void print() = 0;
};
Base::~Base() {}
Base& Base::operator=(const Base&) {
std::cout << "Base& Base::operator=(const Base&)\n"; // nothing real to copy here
return *this;
}
Base& Base::operator=(Base&&) {
std::cout << "Base& Base::operator=(Base&&)\n"; // nothing real to move here
return *this;
}
//----------------------------------------------------------------------------------
struct der_1 : public Base {
der_1(const std::string& value) : Base(), m_value(value) {
std::cout << "der_1(" << m_value << ") converting\n";
}
der_1(const der_1& rhs) : Base(rhs), m_value(rhs.m_value) {
std::cout << "der_1(" << m_value << ") copy\n";
}
der_1(der_1&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
std::cout << "der_1(" << m_value << ") move\n";
}
~der_1() { std::cout << "~der_1(" << m_value << ")\n"; }
der_1& operator=(const der_1& rhs) {
std::cout << "der_1& der_1::operator=(const der_1&)\n";
if(this == &rhs) return *this; // no self-assignment
Base::operator=(rhs); // copy the Base part of rhs
m_value = rhs.m_value; // copy the der_1 specific part
return *this;
}
der_1& operator=(der_1&& rhs) {
std::cout << "der_1& der_1::operator=(der_1&&)\n";
Base::operator=(std::move(rhs)); // move the Base part of rhs
m_value = std::move(rhs.m_value); // move the der_1 specific part
return *this;
}
// override Base's copy assignment
Base& operator=(const Base& rhs) override {
std::cout << "Base& der_1::operator=(const Base&)\n";
// downcasting may throw bad_cast
const der_1& rhsref = dynamic_cast<const der_1&>(rhs);
return *this = rhsref; // call standard copy assignment
}
// override Base's move assignment
Base& operator=(Base&& rhs) override {
std::cout << "Base& der_1::operator=(Base&&)\n";
// downcasting may throw bad_cast
der_1& rhsref = dynamic_cast<der_1&>(rhs);
return *this = std::move(rhsref); // call standard move assignment
}
void print() override { std::cout << "der_1::print(" << m_value << ")\n"; }
private:
std::string m_value;
};
//----------------------------------------------------------------------------------
struct der_2 : public Base {
der_2(int value) : Base(), m_value(value) {
std::cout << "der_2(" << m_value << ") converting\n";
}
der_2(const der_2& rhs) : Base(rhs), m_value(rhs.m_value) {
std::cout << "der_2(" << m_value << ") copy\n";
}
der_2(der_2&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
std::cout << "der_2(" << m_value << ") move\n";
}
~der_2() { std::cout << "~der_2(" << m_value << ")\n"; }
der_2& operator=(const der_2& rhs) {
std::cout << "der_2& der_2::operator=(const der_2&)\n";
if(this == &rhs) return *this; // no self-assignment
Base::operator=(rhs); // copy the Base part of rhs
m_value = rhs.m_value; // copy the der_2 specific part
return *this;
}
der_2& operator=(der_2&& rhs) {
std::cout << "der_2& der_2::operator=(der_2&&)\n";
Base::operator=(std::move(rhs)); // move the Base part of rhs
m_value = std::move(rhs.m_value); // move the der_2 specific part
return *this;
}
// override Base's copy assignment
Base& operator=(const Base& rhs) override {
std::cout << "Base& der_2::operator=(const Base&)\n";
// downcasting may throw bad_cast
const der_2& rhsref = dynamic_cast<const der_2&>(rhs);
return *this = rhsref; // call standard copy assignment
}
// override Base's move assignment
Base& operator=(Base&& rhs) override {
std::cout << "Base& der_2::operator=(Base&&)\n";
// downcasting may throw bad_cast
der_2& rhsref = dynamic_cast<der_2&>(rhs);
return *this = std::move(rhsref); // call standard move assignment
}
void print() override { std::cout << "der_2::print(" << m_value << ")\n"; }
private:
int m_value;
};
//----------------------------------------------------------------------------------
int main() {
myArr<std::unique_ptr<Base>> base_arr;
{
{
std::cout << "-- put pointers to objects of derived classes in base_arr --\n";
base_arr.emplace_back(std::make_unique<der_1>("howdy"));
base_arr.emplace_back(std::make_unique<der_2>(10));
std::cout << "\n-- print what we've got --\n";
for(auto& b : base_arr) b->print();
std::cout << "\n-- set new value for an existing object, by copying --\n";
der_1 something_to_copy("something_to_copy");
*base_arr[0] = something_to_copy;
std::cout << "\n-- set new value for an existing object, by moving --\n";
*base_arr[0] = der_1("something_to_move");
std::cout << "\n-- try to assign a der_2 to a der_1 --\n";
try {
*base_arr[0] = der_2(666);
} catch(const std::exception& ex) {
std::cout << "Exception: " << ex.what() << "\n";
}
std::cout << "\n-- replace a der_1 object with a der_2 object --\n";
base_arr[0] = std::make_unique<der_2>(20);
std::cout << "\n-- destroying something_to_copy since it goes out of "
"scope --\n";
}
std::cout << "\n-- stuff in base_arr --\n";
for(auto& b : base_arr) b->print();
std::cout << "\n-- extract front, got:\n";
auto ptr = base_arr.extract_front();
ptr->print();
std::cout << "\n-- the above dies, goes out of scope --\n";
}
std::cout << "\n-- base_arr is about to be destroyed --\n";
}
Upvotes: 0
Reputation: 119877
You cannot return an object of an abstract class. You need to return a reference or a pointer, preferably a smart one.
You cannot store an object of an abstract class. You need to store a reference or a pointer, preferably a smart one.
You cannot pass an object of an abstract class to a function as a parameter. You need to pass a reference or a pointer, preferably a smart one.
See the pattern?
Objects of abstract classes do not exist on their own. They only exist as subobjects of derived class objects, and can only be accessed via references or pointers obtained from pointers to said derived class objects.
There is no way around it. You cannot do it differently. Use smart pointers. They are the right tool for tthe job.
I want to return something to which I can assign a derived object .
This is kind of possible, depending on what exactly you want, but way more complicated than necessary. Assignment and polymorphic hierarchies don't mix well. I would not recommend that, especially if you are not sure what exactly you want. Assign, store, and pass around smart pointers.
Upvotes: 1