首页 > 其他分享 >函数模板_构造函数栈溢出

函数模板_构造函数栈溢出

时间:2023-09-25 22:12:03浏览次数:36  
标签:std __ Task 模板 func forward inline 溢出 构造函数

前言

最近写一个任务队列,可以支持存入返回值为void的任意函数对象。需要定义一个Task模板,来存储函数对象以及参数。大致的实现如下:

class Task
{
public:
    template <typename Func, typename... Args>
    Task(Func&& f, Args &&...args)
        : func_(std::bind(std::forward<Func>(f), std::forward<Args>(args)...)) {}

    void operator()()
    {
        func_();
    }
private:
    std::function<void()> func_;
};

其中构造函数是一个函数模板,可以在编译的时候,根据传入的函数对象和参数,绑定生成std::function,存储在func_中。

支持形如

auto f1 = [](int i, int j)
{
    std::cout << i << j;
};
auto f2 = [](int i, double j)
{
    std::cout << i << j;
};
Task t(f1, 5, 6);
Task t2(f2, 1, 2.3);

复制栈溢出

但下面这个普通的“拷贝”,linux编译正常,用clang编译器的话会造成栈溢出。
auto t3 = t;
用vs看调用堆栈,发现一直在执行Task::<Task&>(Task & f)--->bind---- > binder等等,一直在执行构造函数。

我尝试自定义拷贝构造,并在其中输出。但发现拷贝函数根本没有执行,而是在反复执行函数模板。

Task(const Task & other) :func_(other.func_)
{
    std::cout << "copy ctor";
}

为什么没有调用拷贝构造

仔细研究发现调用堆栈,发现调用的是Task::<Task&>(Task& f),这并不是拷贝构造,这是函数模板自动生成的。

在https://cppinsights.io/这个网址,提供了编译结果,可以看模板生成了那些函数。

#include <functional>
#include<iostream>

class Task
{

public:
    template<typename Func, typename ... Args>
    inline Task(Func&& f, Args &&... args)
        : func_{ std::bind(std::forward<Func>(f), std::forward<Args>(args)...) }
    {
    }


    /* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<__lambda_29_13&, int, int>(__lambda_29_13& f, int&& __args1, int&& __args2)
        : func_{ std::function<void()>(std::bind(std::forward<__lambda_29_13&>(f), std::forward<int>(__args1), std::forward<int>(__args2))) }
    {
    }
#endif



    /* First instantiated from: insights.cpp:38 */
#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<__lambda_33_13&, int, double>(__lambda_33_13& f, int&& __args1, double&& __args2)
        : func_{ std::function<void()>(std::bind(std::forward<__lambda_33_13&>(f), std::forward<int>(__args1), std::forward<double>(__args2))) }
    {
    }
#endif



    /* First instantiated from: insights.cpp:39 */
#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<Task&>(Task& f)
        : func_{ std::function<void()>(std::bind(std::forward<Task&>(f))) }
    {
    }
#endif



#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<const Task&>(const Task& f);
#endif



    /* First instantiated from: functional:558 */
#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<Task>(Task&& f)
        : func_{ std::function<void()>(std::bind(std::forward<Task>(f))) }
    {
    }
#endif



#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<const std::_Bind<Task()>&>(const std::_Bind<Task()>& f);
#endif



#ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline Task<std::_Bind<Task()> >(std::_Bind<Task()>&& f);
#endif


    inline void operator()()
    {
        this->func_.operator()();
    }

    inline Task(const Task& other)
        : func_{ std::function<void()>(other.func_) }
    {
        std::operator<<(std::cout, "copy ctor");
    }


private:
    std::function<void()> func_;
public:
    // inline ~Task() noexcept = default;
};




int main()
{

    class __lambda_29_13
    {
    public:
        inline /*constexpr */ void operator()(int i, int j) const
        {
            std::cout.operator<<(i).operator<<(j);
        }

        using retType_29_13 = void (*)(int, int);
        inline constexpr operator retType_29_13 () const noexcept
        {
            return __invoke;
        };

    private:
        static inline /*constexpr */ void __invoke(int i, int j)
        {
            __lambda_29_13{}.operator()(i, j);
        }

    public:
        // inline /*constexpr */ __lambda_29_13 & operator=(const __lambda_29_13 &) /* noexcept */ = delete;
        // inline /*constexpr */ __lambda_29_13(const __lambda_29_13 &) noexcept = default;
        // inline /*constexpr */ __lambda_29_13(__lambda_29_13 &&) noexcept = default;

    };

    __lambda_29_13 f1 = __lambda_29_13{};

    class __lambda_33_13
    {
    public:
        inline /*constexpr */ void operator()(int i, double j) const
        {
            std::cout.operator<<(i).operator<<(j);
        }

        using retType_33_13 = void (*)(int, double);
        inline constexpr operator retType_33_13 () const noexcept
        {
            return __invoke;
        };

    private:
        static inline /*constexpr */ void __invoke(int i, double j)
        {
            __lambda_33_13{}.operator()(i, j);
        }

    public:
        // inline /*constexpr */ __lambda_33_13 & operator=(const __lambda_33_13 &) /* noexcept */ = delete;
        // inline /*constexpr */ __lambda_33_13(const __lambda_33_13 &) noexcept = default;
        // inline /*constexpr */ __lambda_33_13(__lambda_33_13 &&) noexcept = default;

    };

    __lambda_33_13 f2 = __lambda_33_13{};
    Task t = Task(f1, 5, 6);
    Task t2 = Task(f2, 1, 2.2999999999999998);
    Task t3 = Task(t);
    return 0;
}

可以看到最后t3是这么生成的。
Task t3 = Task(t);
而Task类中有两个形如这样的函数;

//这个是拷贝构造函数

inline Task(const Task& other)
    : func_{ std::function<void()>(other.func_) }
{
    std::operator<<(std::cout, "copy ctor");
}

//这个是函数模板生成的一个构造函数,参数为Task &f。

#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline Task<Task&>(Task& f)
    : func_{ std::function<void()>(std::bind(std::forward<Task&>(f))) }
{
}
#endif

这下明白为什么没有调用拷贝构造了,原来Task t3 = Task(t)中,等号右边的Task(t)并不是拷贝构造函数。因为t是非常量左值,所以编译器优先匹配模板函数的参数(Task & )。

为什么会栈溢出

接下来是为什么这个模板函数会递归构造,直至栈溢出。

观察这个模板函数,发现其形参中有std::bind。而这个函数会复制拷贝传入参数(这里就是Task)。而复制Task并不会调用构造函数,而是调用这个函数模板,因此,一直递归调用直至栈溢出。

解决

既然非常量左值匹配不上拷贝构造,那就把返回值转换成常量左值, 改成下面这种形式。就没问题了

auto t3 = static_cast<Task&>(t);

标签:std,__,Task,模板,func,forward,inline,溢出,构造函数
From: https://www.cnblogs.com/chendasxian/p/17728872.html

相关文章

  • idea java代码注释模板制作 idea类注释模板设置【转载】
    一、类模板设置1、进入设置页面:File-->settings-->Editor-->FileandCodeTemplates-->Files2、设置类、接口、枚举模板信息3、点击Apply应用设置二、方法模板设置1、同样打开设置:File-->settings-->Editor-->LiveTemplates2、新建模板组:命名为userDefine3、选中新建的模板组,新......
  • P3812 【模板】线性基
    题意给定\(n\)个整数,求这\(n\)个整数的异或最大值。Sol线性基模板题。考虑维护一个线性基。插入一个数时,从高位往低位枚举。遇到第一个基中不存在的位,就将该数加入基,否则异或下去。询问最大值,考虑贪心,若当前\(ans^p[i]>ans\)则直接\(ans^=p[i]\)。#include<i......
  • java项目开发常用配置文件模板
    mybatisconfig文件1<?xmlversion="1.0"encoding="UTF-8"?>2<!DOCTYPEconfiguration3PUBLIC"-//mybatis.org//DTDConfig3.0//EN"4"http://mybatis.org/dtd/mybatis-3-config.dtd">5......
  • 全局数组未加锁访问溢出导致才内存
    在客户那里发现有些数据包被错误的转到了standbySMM上,后面查看proc发现是knet.ko中的role字段被踩后面再检查发现有三个字段都被踩:zyc@fishsmm_arm64(/≧▽≦)/~/do_not_remove/aarch64-marvell-linux-gnu-nmlinux-casa-knet.ko|grepsmm_role0000000006925110B......
  • chart模板实战
    参考:https://helm.sh/zh/docs/chart_template_guide/getting_started/https://helm.sh/zh/docs/chart_template_guide/function_list/一.入门chart1.创建一个charthelmcreatemychart查看目录结构[root@k8s-masterhelm-test]#treemychart/mychart/├──charts├......
  • two-sat模板
    P4782【模板】2-SAT问题就是给关系进行连边,然后判断是否存在矛盾输出方案的时候,就是在拓扑图上沿着反边走,但实际上tarjan求强连通分量已经排好序了编号小的scc就是在拓扑序中排在后面的强连通分量#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#......
  • 使用 goland 的模板提高编码效率
    整体步骤来自chatgpt概述我觉得编译器有几个很提效的工具:快捷键、代码补全和代码模板。前两个没啥可说的,今天想分享的是代码模板。在Goland里被称之为LiveTemplates。在代码里输入forr,随后会出现如下的可选项,选中按下回车后,会自动生活一个forrange的遍历模板,通过ta......
  • 如何在 SOLIDWORKS中创建零件模板 硕迪科技
    作为一款多功能且可大量定制的3DCAD软件,SOLIDWORKS模板可以通过自定义属性包含大量数据。可以通过为SOLIDWORKS零件、装配体和工程图创建模板来利用这些模板。与其他一些CAD软件不同,SOLIDWORKS不限制您可以创建的模板数量-您可以根据需要创建任意数量的零件、装配体和工程图模......
  • 基于方向编码的模板匹配算法matlab仿真
    1.算法运行效果图预览  2.算法运行软件版本MATLAB2022a 3.算法理论概述       模板匹配是一种常见的计算机视觉方法,用于在一幅图像中寻找指定的模板。它在目标检测、图像识别、物体跟踪等领域中有广泛的应用。基于方向编码的模板匹配算法是一种改进的模板......
  • 【模板】多项式乘法、乘法逆、除法、取模、常系数齐次线性递推
    以下代码必须开-O2#include<algorithm>#include<cassert>#include<cstdio>#include<cstring>#include<vector>usingnamespacestd;#ifdefLOCAL#definedebug(...)fprintf(stderr,##__VA_ARGS__)#else#definedebug(...)void(0)#......