I am trying to find a way to make something similar to this work:
class A {
public:
int x;
};
class A1 : public A {
public:
int y;
};
class A2 : public A {
public:
std::string s;
};
void printer(A1 A1obj){
std::cout << (A1obj.y+1) << std::endl;
}
void printer(A2 A2obj){
std::cout << A2obj.s << std::endl;
}
void printall(A Aobj){
printer(Aobj);
}
In words: I have some code that works with A objects. many functions in my code take A objects as arguments, making no use of the y or s components. however eventually they will call a couple of functions that will behave differently according to whether the input A was really A1 or A2. I was thinking that I could "overload" these, considering both A1 and A2 are children of A. But the above code says no matching function for call to printer(A&)
One solution is to duplicate printall into printall(A1 A1obj) and printall(A2 A2obj) and use function overloading, but this would mean duplication of a many lines of code in my case, so I'd like to avoid it. Are there alternatives other than just merging A1 and A2 into A and, say, create a std::string label inside it to be used with if-else statements?
Option 1: Virtual functions ("run-time polymorphism")
Whatever makes sense as a conceptual thing to do with/to an A, but the details of what that means will vary by specific class, should be a virtual function of A.
class A {
public:
virtual ~A() = default;
// Disable copying to avoid accidental slicing:
A(const A&) = delete;
A& operator=(const A&) = delete;
virtual void print() const = 0;
int x;
};
class A1 : public A {
public:
void print() const override;
int y;
};
class A2 : public A {
public:
void print() const override;
std::string s;
};
void A1::print() const {
std::cout << y+1 << std::endl;
}
void A2::print() const {
std::cout << s << std::endl;
}
void printall(const A& Aobj) {
Aobj.print();
}
Each derived class A1 and A2 overrides the virtual function declared in A, so calling the function via an A reference or pointer will actually call the definition from the derived class.
Option 2: A template function ("compile-time polymorphism")
// Classes and printer overloads as in question
template <class T>
void printall(const T& Aobj) {
printer(Aobj);
}
Define a template function which can take an object of any type, and generates a function for that type as needed. Here T could really be anything at all, not necessarily a class which inherits A, as long as it can be passed to some printer function.
But if there are other printall functions and that declaration makes it too greedy in overload resolution, you can restrict it to only take types that inherit A.
If you can use C++20 "constraints and concepts":
#include <concepts>
template <class T> requires std::derived_from<T, A>
void printall(const T& Aobj) {
printer(Aobj);
}
Otherwise, you'd need something a little trickier:
// C++11 or later:
#include <type_traits>
template <class T>
typename std::enable_if<std::is_base_of<A, T>::value>::type
printall(const T& Aobj) {
printer(Aobj);
}
This has nothing to do with ambiguous overloading. Even if there was only one printer() function, and one child class, this will never work. If you have a parent class, A, it cannot be implicitly converted to any child class, like A1. You can implicitly convert a child class to a parent class, but not the other way around. This is the only way C++ works.
The correct way is to define an abstract print() method in the A superclass, and then implement it in both A1 and A2 subclasses, to invoke the appropriate printer() function. This is precisely why virtual methods exist. Or, just implement each printer() in the subclass directly, as a print() method.
Related
UPDATE: the behaviour is not template specific, so
struct Derived : public Base {
OverrideFoo* m_data {new OverrideFoo()};
}
will do the same. so it seems m_data in Derived and m_data in Base both exist in memory layout. If we define a function in Derived,
e.g., Derived::print() { m_data->print()}, this will use m_data in Derived, however, if base function is called on derived object, it still use m_data from Base.
I was surprised with the behaviour of the following code, it prints "from foo", rather than the "from override foo". why is it like this? shouldn't the "m_data" be the type of "OverrideFoo"?
#include <iostream>
using namespace std;
struct Foo {
void print() {
printf("from foo\n");
}
};
struct OverrideFoo {
void print() {
printf("from override foo\n");
}
};
struct Base {
void useData() {
m_data->print();
}
Foo* m_data {new Foo()};
};
template <class t>
struct Derived : public Base {
t* m_data {new t()};
};
int main()
{
Derived<OverrideFoo> d;
d.useData();
return 0;
}
When you call d.useData(), you are calling Base::useData(), which accesses Base::m_data.
I suppose you're expecting Base::useData() to use Derived::m_data, just because the variable has a similar name. However that's not how this works. Both classes get their own independent m_data, and in your case, with different types.
It's true that Derived::m_data hides Base::m_data, which may suggest to you that they are related or that one "overrides" the other. Don't confuse that with hiding. The hiding is a natural consequence of the similar naming. If Derived needs to access Base::m_data, it must qualify it in order to disambiguate from its own m_data.
Note: Member variables / fields cannot be overridden. If you need an override-style behavior, you'll need to do it via a member function (something like virtual IPrintable* GetPrintable(). And the base class must grant the possibility of overriding with the virtual keyword.
Another way to think about this: Base, despite what its name suggests, is a complete type. You can do Base x; to instantiate and use this class, without being derived. The compiler generates code for Base which is complete and functional, including the code to access Base::m_data. If m_data were somehow overrideable, how could this code be generated? What would Base understand sizeof(*m_data) to be, if its datatype could be overridden in some base class? How would the compiler know what m_data even refers to, if you're suggesting it can be changed by any class which derives it?
Another point: If members were able to be overridden by default (without the virtual keyword), it would cause mass chaos for base classes. Imagine writing a generic base class and risking that derived classes could unknowingly change the state of the base? Or imagine writing a derived class and being concerned about your variable naming because "well maybe a base class used the same name?"
So let's summarize the key points:
Fields cannot be overridden, period. It would break sizeof() among lots of other things (whole other topic)
Base classes must explicitly grant derived classes to override member functions via the virtual keyword.
There are probably better ways to do what you're attempting though. The most natural for me would be to specify the Foo type as a template parameter to Base.
Like this:
struct Foo1 {
void print() {
printf("from foo\n");
}
};
struct Foo2 {
void print() {
printf("from override foo\n");
}
};
template<typename TData>
struct Base {
void useData() {
m_data.print();
}
TData m_data;
};
template <typename TData>
struct Derived : public Base<TData> {
};
int main()
{
Derived<Foo1> d1;
d1.useData();
Derived<Foo2> d2;
d2.useData();
return 0;
}
It's hard to know the best approach for you, because this is an unrealistic contrived example.
Try this code out and you will find that the two m_data has different memory address, which means they are different variable.
#include <iostream>
using namespace std;
struct Foo {
void print() {
printf("from foo\n");
}
};
struct OverrideFoo {
void print() {
printf("from override foo\n");
}
};
struct Base {
void useData() {
m_data->print();
std::cout << m_data << std::endl;
}
Foo* m_data {new Foo()};
};
template <class t>
struct Derived : public Base {
t* m_data {new t()};
};
int main()
{
Derived<OverrideFoo> d;
d.useData();
d.m_data->print();
std::cout << d.m_data << std::endl;
return 0;
}
I have two base classes A, and B, and a third class C that (virtually) derives from both of them. Each class exposes its own public shared_ptr type.
In another class I have two vectors where I want to add objects of type A to one vector, objects of type B to another vector, and objects of type C to both. This results in three add methods, one for each of those three classes.
My problems arise when I try to further derive from C:
#include <iostream>
#include <memory>
#include <vector>
class A {
public:
using shared_ptr = std::shared_ptr<A>;
virtual ~A() {};
};
class B {
public:
using shared_ptr = std::shared_ptr<B>;
virtual ~B() {};
};
class C : virtual public A, virtual public B {
public:
using shared_ptr = std::shared_ptr<C>;
virtual ~C() {};
};
class D : virtual public C {
public:
virtual ~D() {};
};
class Test {
protected:
std::vector<A::shared_ptr> vecA;
std::vector<B::shared_ptr> vecB;
public:
void add(const A::shared_ptr& o) {
std::cerr << "in A" << std::endl;
vecA.push_back(o);
}
void add(const B::shared_ptr& o) {
std::cerr << "in B" << std::endl;
vecB.push_back(o);
}
void add(const C::shared_ptr& o) {
std::cerr << "in C" << std::endl;
vecA.push_back(o);
vecB.push_back(o);
}
};
int main()
{
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
auto c = std::make_shared<C>();
auto d = std::make_shared<D>();
Test t;
t.add(a);
t.add(b);
t.add(c);
t.add(d);
}
This doesn't work - the resolution of which version of add to call cannot be determined:
test.cc:62:7: error: call to member function 'add' is ambiguous
t.add(d);
~~^~~
test.cc:34:10: note: candidate function
void add(const A::shared_ptr& o) {
^
test.cc:39:10: note: candidate function
void add(const B::shared_ptr& o) {
^
test.cc:44:10: note: candidate function
void add(const C::shared_ptr& o) {
^
I do have the option of simply passing my C object separately to both Test::add(const A::shared_ptr&) and Test::add(const B::shared_ptr&) because in reality the B version of add has additional parameters that resolve the overload but I would prefer that the caller not have to remember to do this.
Is this ambiguity resolvable? My target environment constrains me to C++14.
The standard derived-to-base conversion sequences take the length of the inheritance chain into account when ranking conversion sequences, a close base would be deemed a better conversion sequence than a one that is further up the inheritance chain. And that in turn affects pointers and references too!
Sadly, since smart pointers are user defined types, they cannot benefit from this behavior. All three overloads are viable via a (valid) user defined conversion. And the "ranks" of the individual bases don't affect the ranking of the overloads.
But that doesn't mean we can't re-introduce the ranking impose by a derived-to-base conversion. We just need to do so via another argument. And by employing tag-dispatch, we can do just that.
We can define a helper utility type:
template<int n> struct rank : rank<n - 1> {};
template<> struct rank<0> {};
For any 0 <= i < j <= k, the conversion sequence of rank<k> -> rank<j> will always be deemed better than rank<k> -> rank<i>. So, if we make your overload set inaccessible, and rank them explicitly:
protected:
void add(const A::shared_ptr& o, rank<0>) { /*...*/ }
void add(const B::shared_ptr& o, rank<0>) { /*...*/ }
void add(const C::shared_ptr& o, rank<1>) { /*...*/ }
We can then expose another overload in the form of a function template:
public:
template<typename T>
void add(const std::shared_ptr<T>& o) {
return add(o, rank<10>{});
}
It mainly just forwards to one of the protected overloads, but it adds another argument. A rank tag. This will affect overload resolution too. Even though all three add overloads are viable, the derived-to-base conversion of rank<10> will affect the choice of best one.
Here it is live.
Is it possible to acces a member of a derived class using a pointer to the base class?
// Example program
#include <iostream>
#include <vector>
#include <memory>
#include <string>
class A {
public:
std::string x = "this is the wrong x\n";
};
template <class T>
class B : public A {
public:
T x;
};
int main()
{
std::vector<std::unique_ptr<A>> vector;
auto i = std::make_unique<B<int>>();
i->x = 6;
vector.push_back(std::move(i));
for(auto &element : vector){
std::cout << element->x;
}
}
Here I'm always getting the output from class A. I cannot typecast it because I don't know whether the element is of type A or type B in advance. Is there a proper way to do this?
The proper way would be to make a virtual function to perform the task like printing.
class A {
public:
std::string x = "this is the wrong x\n";
virtual ~A() = default;
virtual void print() const { std::cout << x; }
};
template <class T>
class B : public A {
public:
T x;
virtual void print() const override { std::cout << x; }
};
int main()
{
std::vector<std::unique_ptr<A>> vector;
auto i = std::make_unique<B<int>>();
i->x = 6;
vector.push_back(std::move(i));
for(auto &element : vector){
element->print();
}
}
If you have a pointer to a base class, you can only access things defined on that base class (without typecasting). For all the compiler knows, it is an instance of the base class and has nothing else.
Polymorphic behavior involves using virtual functions - derived classes can change which function is called when invoking a virtual function of the base class. Note that this mechanism does not exist for members (what would you change about a member? There's only the type, and changing that in a derived class makes no sense). So the only meaningful thing you can do with pointers to base classes that should have customized behavior is to call their virtual functions.
Now, you could think "ok, I'll just make access to x go through a virtual function", but the problem here is that you must specify the involved types when you declare the virtual function in the base class already. That makes sense: The compiler needs to know which types a function involves, even a virtual one. You may only pass and return different types in the overriding functions if they are "compatible" - see covariance and contravariance for more information.
So unless all your T are covariant, virtual functions cannot help you either.
The core flaw with your concept is that you want to have some type (i.e. element->x) in a non-templated function depend on the dynamic type of some object (i.e. element). That is impossible because the compiler must know the type of each expression at compile-time. So you must approach your problem differently.
I am confused how the class inherits from the class RecursiveASTVisitor by passing itself as a template argument. Also, does writing Rewrite(R) in the line
MyRecursiveASTVisitor(Rewriter &R) : Rewrite(R) { }
assign the value R to the variable Rewrite? There is no class Rewrite defined anywhere in the code. Is the ":" operator used for things other than inheriting from a class?
class MyRecursiveASTVisitor
: public RecursiveASTVisitor<MyRecursiveASTVisitor>
{
public:
MyRecursiveASTVisitor(Rewriter &R) : Rewrite(R) { }
void InstrumentStmt(Stmt *s);
bool VisitStmt(Stmt *s);
bool VisitUnaryOperator(UnaryOperator *e);
Rewriter &Rewrite;
};
Its called curiously recurring template pattern. When compiler creates RecursiveASTVisitor<MyRecursiveASTVisitor> it knows layout of MyRecursiveASTVisitor so its all OK.
You can read more on wikipedia
As the comments mentioned, this is known as the Curiously Recurring Template Pattern. This pattern is often implemented to provide a mechanism similar to virtual functions, but at compile time (static polymorphism). For example, RecursiveASTVistor<T> might contain a method that does the following:
...
//using T = MyRecursiveASTVisitor; for your specific case
T *concrete_visitor = static_cast<T*>(this);
concrete_visitor->VisitStmt(something);
If VisitStmt is defined in your MyRecursiveASTVisitor class, then that method is called, otherwise it calls the base definition provided by RecursiveASTVistor. Callers outisde of your class hierarchy also get to take advantage of this static polymorphism.
Here is a short example to help you gain a better intuition of what's happening:
#include <iostream>
template <class T>
struct Base {
void foo() {
T *concrete = static_cast<T*>(this);
concrete->foo();
};
void bar() {std::cout << "Base" << std::endl; }
};
struct Derived : public Base<Derived> {
void foo() {std::cout << "Derived" << std::endl;}
};
int main() {
Base<Derived> b;
b.foo();
b.bar();
}
Output
Derived
Base
Edit: To answer your additional question:
Also, does writing Rewrite(R) in the line
MyRecursiveASTVisitor(Rewriter &R) : Rewrite(R) { } assign the value
R to the variable Rewrite? There is no class Rewrite defined anywhere
in the code. Is the ":" operator used for things other than inheriting
from a class?
Rewrite is a member variable of your MyRecursiveASTVisitor class and is a reference to an object of type Rewriter. The : operator is used in the definition of a constructor to signify a member initializer list. In this case, we simply initialize the Rewrite variable with the passed in argument R. Just to be clear, MyRecursiveASTVisitor(Rewriter &R) : Rewrite(R) { } is a constructor definition for your class MyRecursiveASTVisitor, it is not the class definition.
Have a base class A, and a derived class B which overrides function template Func:
class A
{
A() {...};
~A() {};
template <class T>
void Func(const String &sInput, T &tResult)
{...}
};
class B : public A
{
B() {...}
~B() {};
template <class T>
void Func(const String &sInput, T &tResult)
{...}
};
(Note that Func is non-virtual, given the lack of support in C++ for templated virtual functions.)
Now have a mainprog API, class M:
class M
{
M(boost::shared_ptr<A> &pInterfaceInput): pInterface(pInterfaceInput)
{}
template <class T>
Evaluate(const String &sInput, T &tResult)
{
pInterface->Func<T>(sInput, tResult);
}
private:
const boost::shared_ptr<A> pInterface;
};
I want the function Evaluate here to support calls to functions on base class A or any of its derived classes (such as B). This class was written with polymorphism in mind before I re-designed class A and B to have templated functions.
Now the problem here is that if I pass a shared pointer of the base type to the derived type then Func of the base class will be called, not the derived class being pointed to.
How do I get around the lack of dynamic polymorphism here?
I've considered making class M a class template on the shared pointer type and having a static_cast in the constructor to ensure this type is of the base class type (A) or of a derived class.
What's the nicest way to do this? I'd prefer not to modify classes A and B to get around this problem but all suggestions are welcome.
Thanks.
Sounds like a double dispatch problem. Perhaps this would be a good place to implement the visitor pattern?
For example, create a class Evaluator, and for each T a subclass ConcreteEvaluator<T>. Give A and B methods that visit the Evaluator. Something like:
class Evaluator
{
virtual void visit_A(A* object);
virtual void visit_B(B* object);
};
template <typename T>
class ConcreteEvaluator : public Evaluator
{
public:
String* input_reference;
T& result_reference;
ConcreteEvaluator(String& input_reference_,T& result_reference_) :
input_reference(input_reference_),
result_reference(result_reference_) {}
virtual void visit_A(A* object) {
object->Func(input_reference,result_reference);
}
virtual void visit_B(B* object) {
object->Func(input_reference,result_reference);
}
}
class A
{
...
virtual void apply_evaluator(Evaluator *eval) {eval->visit_A(this);}
...
}
class B
{
...
virtual void apply_evaluator(Evaluator *eval) {eval->visit_B(this);}
...
}
For each subclass of A, a new method must be added to ConcreteEvaluator, so that this technique works best if A's class hierarchy is stable. And for each subclass of A, it must have an apply_evaluator function defined properly.
On the other hand, this may be total overkill. For about the same amount of work, you could always just pay the price to update M::Evaluate:
class M
{
...
void Evaluate(const String& sInput, T& tResult)
{
// try to downcast to each subclass of A. Be sure to check
// sub-subclasses first
try
{
dynamic_cast<B*>(pInterface.get())->Func(sInput, tResult);
return;
}
catch (std::bad_cast& ) { }
...
// nothing worked. It must really be an A
pInterface->Func(sInput,tResult);
}
...
};
I've show in the question Templatized Virtual function how to use type erasure to get some of the effects of virtual member function. Depending on what you want to do in Func(), you can use the same technique here.