I would like to achieve something like following on c++14, basically derived class can have different type of return type (e.g int, double, string, etc)
class Base {
public:
virtual auto value() = 0; // I know this won't compile
};
class Derived1 : public Base {
public:
double value() override { return 1.0; };
};
class Derived2 : public Base {
public:
int value() override { return 1; };
};
I know above code won't compile but I'm trying to achieve something like that using any possible way or pattern (I've tried template, CRTP, visitor but nothing can satisfy my following code)
Derived1 d1;
Derived2 d2;
std::vector<Base*> base = { &d1, &d2 };
for (const auto* b : base)
std::cout << b->value();
The best I can get with template is something like
Derived1 d1;
Derived2 d2;
std::vector<Base*> base = {&d1, &d2);
for (const auto* b : base)
if (dynamic_cast<Derived1>(b))
std::cout << b->value<double>();
else if (dynamic_cast<Derived2>(b))
std::cout << b->value<int>();
but if I have 100 types of Derived class, it won't look that pretty :D
This is not possible, fundamentally, in C++. C++ does not work this way, for the following simple reason. Let's just pretend that this works somehow. Consider the following simple function:
void my_function(Base *p)
{
auto value=p->value();
}
Now, ask yourself: what is value
's type, here? You may not be aware of this, but there is no such actual type called auto
in C++. auto
is a placeholder for the C++ compiler to deduce, or determine the actual type at compile time. auto
basically says: whatever the expression's type evaluates to be, that's the type of this object. If your C++ compiler determined that p->value()
returns an int
, then value
is an int, and the above is 100% equivalent to declaring int value=p->value();
.
Here, it is impossible to determine value
s actual type. Is it an int
? Is it a double
? Or something else?
Unfortunately, it's a mystery that will remain unsolved forever. The actual value of type
depends on the derived object that the pointer to the Base
actually points to, which is unknown at compile-time, and can only be determine at run time.
It is a fundamental property of C++ that the types of all objects must be deduced at compile time. This is baked-into C++. There are no workarounds. There are no alternatives. What you are trying to do cannot be done in C++.
However, there's a little bit of good news: if the number of possible return types is limited, just declare an ordinary virtual method that returns a std::variant
, and each derived class can then return an appropriate value. It will be up to the caller to make use of it.
In the case above, this would be:
class Base {
public:
virtual std::variant<int, double> value() = 0;
};
If the type of the actual value being returned is completely unknown, then I suppose you can use std::any
. In either case, as you attempt to implement either approach you will discover that C++ will force you to figure out and check for each possible type (in ways that depend on whether you use std::variant
or std::any
), each time you attempt to use the value returned from this method.