Child class' dynamic method calls parent class' virtual method when used in lambda capture / results in segmentatin fault

Viewed 93

Edit: with final keyword on the implementations of the virtual function results in printing the correct string, but why is the final keyword here needed? Could somebody explain?

I am tinkering with variadic templates, I have pretty generic classes D1, D2, D3, ... and they all derive from a class A. Each class has a static and a dynamic print functions, the parent class has a virtual dynamic print function for dynamic dispatching. When I try to reproduce it on a single file:

class A { 
  public:
  virtual void printDynamic();
  static void printStatic();
}

class D1 : public A {
  public:
  virtual void printDynamic();
  static void printStatic();
}

And I have following variants:

std::variant<A,As...> apvar;
std::variant<A*,As*...> avar;

I instantiate both variants with all the derived classes D1,D2,... (I know upcasting Pointers i just want to dereference to their types and do random stuff)

I have implemented recursive visitors for the wrappers, i need to capture this because I encapsulated most of the functions in a class, when I call the visitor on classes I get the names "DX", "DX" ; X corresponding to 1.

    template<class X, class Y, class... Zs>
    void visit_actor(){
        std::visit(
            [this](auto&& value){
                if constexpr(std::is_same<expr_type<decltype(value)>,expr_type<X>>::value){
                    value.printStaticName();
                    value.printDynamicName();
                } else{
                    visit_actor<Y,Zs...>();
                }
            }, avar
        );
    }

But if I call the visitor on pointer variants and when I call the functions: For the static function I get: "DX" with X corresponding to I, but when I call the dynamic function I get the name: "Abstract A".

    template<class X, class Y, class... Zs>
    void visit_pointer(){
        std::visit(
            [this](auto&& value){
                if constexpr(std::is_same<expr_type<decltype(value)>,expr_type<X>>::value){
                    value->printStaticName();
                    value->printDynamicName();
                } else{
                    visit_pointer<Y,Zs...>();
                }
            }, apvar
        );
    }

I have tried reading about it in c++ documentation but could not find the reason yet. What could be the reason that the static function of the derived class is called but the parents virtual function is called ?

For A Minimal Producable example, you need to instantiate the classes:


#include <variant>
#include <iostream>
#include <string>

template <class T>
using expr_type = std::remove_cv_t<std::remove_reference_t<T>>;

template<class A,class... As>
class ActorWrapper{
    public:
    std::variant<A,As...> var;
    template<class Ins>
    ActorWrapper(Ins ins) : var(ins) {}
};

template<class A,class... As>
class ActorPointer{
    public:
    std::variant<A,As... > var;
    template<class T>
    ActorPointer(T* t) : var(t) {}
};


class X {
    public:
    int a;

    virtual std::string getDynamicName() {
        return "dynamic X";
    }

    static std::string getStaticName(){
        return "static X";
    }
};

class D1 : public X{
    public:
    bool b;
    std::string getDynamicName()  override {
        return "dynamic D1";
    }

    static std::string getStaticName(){
        return "static D1";
    }
};

class D2: public X {
    public:
    bool b;
    std::string getDynamicName() override {
        return "dynamic D2";
    }

    static std::string getStaticName(){
        return "static D2";
    }
};




template<class A, class... As>
class TemplatedAGraph{
    private:
    //change aw to correspond to ap
    template<class X>
    void visit_actor(){
        std::visit(
            [this](auto&& value){
                if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
                    std::cout << "z" << std::endl;
                    std::cout << value.getStaticName() << std::endl;
                    std::cout << value.getDynamicName() << std::endl;
                }else{
                    std::cout << "d" << std::endl;
                    return;
                }
            }, aw.var
        );
    }

    template<class X, class Y, class... Zs>
    void visit_actor(){
        std::visit(
            [this](auto&& value){
                if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
                    std::cout << "x" << std::endl;
                    std::cout << value.getStaticName() << std::endl;
                    //std::cout << value.getDynamicName() << std::endl;
                } else{
                    std::cout << "y" << std::endl;
                    visit_actor<Y,Zs...>();
                }
            }, aw.var
        );
    }

    template<class X>
    void visit_pointer(){
        std::visit(
            [this](auto&& value){
                if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
                    std::cout << "a" << std::endl;
                    std::cout << value->getStaticName() << std::endl;
                    std::cout << value->getDynamicName() << std::endl;
                }else{
                    std::cout << "b" << std::endl;
                    return;
                }
            }, ap.var
        );
    }

    template<class X, class Y, class... Zs>
    void visit_pointer(){
        std::visit(
            [this](auto&& value){
                if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
                    std::cout << "c" << std::endl;
                    std::cout << value->getStaticName() << std::endl;
                    std::cout << value->getDynamicName() <<std::endl;
                } else{
                    //std::cout << typeid(decltype(value)).name() <<std::endl;
                    //std::cout << typeid(X).name() <<std::endl;
                    //std::cout << std::is_same_v<decltype(value),X> << std::endl;
                    std::cout << "d" << std::endl;
                    visit_pointer<Y,Zs...>();
                }
            }, ap.var
        );
    }

    public:
    ActorPointer<A*,As*...> ap;
    ActorWrapper<A,As...> aw;

    void print_names(){
        visit_actor<A,As...>();
    }

    void print_names_w_pointer(){
        visit_pointer<A*,As*...>();
    }

    //change ap to coresspond to aw

    template<class X>
    TemplatedAGraph(X a) : ap(&a), aw(a) {}

};

int main(){
    D2 d2;
    D2* d2ref = &d2;
    std::cout << d2ref->getDynamicName() << std::endl;
    TemplatedAGraph<D1,D2> tag(d2);
    tag.print_names();
    tag.print_names_w_pointer();
}

The Output is:

thrud@thrud ~/wörk/test $ g++ main.cpp -std=c++17
thrud@thrud ~/wörk/test $ ./a.out 

dynamic D2
y
z
static D2
dynamic D2
d
a
static D2
Segmentation fault

So I think I have stumbled upon an undefined behaviour, but still I would like to know the reason.

1 Answers

TemplatedAGraph(X a) : ap(&a), aw(a) {} stores a pointer to a local variable in ap. That pointer becomes dangling soon afterwards. Any attempt to access it then exhibits undefined behavior.

You might have meant TemplatedAGraph(X& a) :.... This way, your code works, as far as I can tell.

Related