c++ - 子类的动态方法在 lambda 捕获中使用时调用父类的虚方法/导致段错误

标签 c++ templates c++17 template-meta-programming

编辑:在虚函数的实现上使用 final 关键字会导致打印正确的字符串,但是为什么这里需要 final 关键字?有人能解释一下吗?

我正在修改可变参数模板,我有非常通用的类 D1、D2、D3……它们都派生自 A 类。每个类都有一个静态和动态打印功能,父类有一个虚拟动态打印功能用于动态调度。当我尝试在单个文件上重现它时:

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

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

我有以下变体:

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

我使用所有派生类 D1、D2、... 实例化这两个变体(我知道向上转换指针我只想取消对它们的类​​型的引用并做一些随机的事情)

我已经为包装器实现了递归访问者,我需要捕获它,因为我将大部分函数封装在一个类中,当我在类上调用访问者时,我得到了名称 "DX", "DX"; X 对应于 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
        );
    }

但是,如果我在指针变体上调用访问者以及调用函数时:
对于静态函数,我得到:“DX”,X 对应于 I,但是当我调用动态函数时,我得到了名称:“抽象 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
        );
    }

我曾尝试在 C++ 文档中阅读它,但还没有找到原因。调用派生类的静态函数但调用父虚拟函数的原因可能是什么?

对于 A Minimal Producable 示例,您需要实例化这些类:

#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();
}


输出是:

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

所以我想我偶然发现了一个未定义的行为,但我仍然想知道原因。

最佳答案

TemplatedAGraph(X a) : ap(&a), aw(a) {}ap 中存储一个指向局部变量的指针.那个指针很快就变得悬空了。任何访问它的尝试都会表现出未定义的行为。

您可能是指 TemplatedAGraph(X& a) :... .这样,your code works ,据我所知。

关于c++ - 子类的动态方法在 lambda 捕获中使用时调用父类的虚方法/导致段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61685921/

相关文章:

C++17 如何保存通用可调用对象以供以后使用

c++ - 使用 vector 数组在 C++ 中创建扫雷

c++ - 在不考虑模板参数的情况下比较模板化类型

C++ 多组可变函数参数

templates - angular.js 中的模板 - 继承

c++ - "main"函数可以用 "noexcept"说明符声明吗?

c++ - period 必须是 C++17 chrono 库中 ratio 的特化吗?

c++ - 在 OpenCV 2 中混合彩色图像与灰度图像

c++ - Qt - 滚动图表的可见区域

c++ - 用于对象检测的 OpenCV .xml 文件复制