c++26新功能—indirect<T>和polymorphic<T>

一、indirect<T> 和 polymorphic<T>

indirect,间接的;polymorphic,多态。前者大家可能还有些生疏,但对后者应该是非常熟悉的。在C++26的模板支持中,提供了两个新类模板即indirect<T>和polymorphic<T>。主要目的是为了更完善的通过特化来实现具体的值语义。注意,是值语义。特别是后者,可以实现多态性质的值与其它标准库类型整体的组合,进而让编译器能够生成相关的函数和接口。
它们二者都是为动态分配对象的一种值语义控制的接口,其常量性都具有传染性。indirect<T>会持有T的对象并在复制indirect<T>时深拷贝T对象;polymorphic<T>有点类似于多态的控制,它持有一个T对象,但复制polymorphic<T>时会复制T的子类对象。
在C++的开发者中,处理多态一般是使用指针或引用。为了区别与指针等的不同,完善标准库中对值语义的相关内容,支持在复合类型中可以更简单方便的实现相关的代码。所以标准库中增加了对此二者的扩展。

二、具体的分析

在使用指针类型时,开发者可能会遇到一个问题,在pimpl中,如果一不小心会遇到非完全类型的的情况。也就是说,C++中提供了间接控制对象的指针类型。但在实际的开发过程中,其实也是需要对象的值类型的。特别是在存在继承的情况下,在标准库中的应用如果想控制值语义,需要通过一些技术方法才可能实现。
在C++26中完善了这方面的技术细节,通过indirect<T> 和 polymorphic<T>来达到相关的设计目的。特别是在复合类的设计中,其具有非常重要的意义 。正如上面据说,这两个类模板都具有常量传播和深拷贝的性质。除此之外,它们还具有一些特点:

  1. 无值状态
    为了支持实现移动语义的相关控制,它们都有一个无值状态。达种状态下,如果进行了移动操作,则可以通过成员函数valueless_after_move来判断是否正常
  2. 分配器支持
    二者均为分配器感知类型,所以其适合于此种类型的容器应用。如std::vector或std::map等
  3. 支持受限的契约扩展
    即支持在运行时的先决条件下的契约开发,但operator-> 和 operator*两个运算符除外
  4. 提供了完美转发运算符
    和optional和variant等一样,提供了完美转发的的相关运算符

需要提醒一下的是,要区别与indirect<T>和variant<Ts…>的不同,variant提供了多种可选类型之一的控制而indirect<T>则提供了单一类型的控制。而与智能指针相比,这两个类模板又提供了值语义和深拷贝且可以正确传播常量。
另外,polymorphic<T>需要派生类必须有公开的拷贝构造函数;其接口基类需要受保护的析构和拷贝构造函数。

三、应用场景

在标准的设计目的中,二者的应用主要有以下场景:

  1. 需要动态存储类型且类型已知的情况下,如上文提到的PIMPL、对象优化或减小头文件依赖等等
  2. 需要在运行时处理多种不同的子类并希望它们以值类型存储在同一容器中时,可以使用polymorphic<T>
  3. 一般是应用在复合类中

四、具体的例程

由于具体的标准尚未正式推出,所以看一下相关的提案中的例程:

struct A {
    enum class Constness { CONST, NON_CONST };
    Constness foo() { return Constness::NON_CONST; }
    Constness foo() const { return Constness::CONST; }
};

class Composite {
    indirect<A> a_;
    Constness foo() { return a_->foo(); }
    Constness foo() const { return a_->foo(); }
};

int main() {
    Composite c;
    assert(c.foo() == A::Constness::NON_CONST);
    const Composite& cc = c;
    assert(cc.foo() == A::Constness::CONST);
}


// std::polymorphic
#include <memory>
#include <vector>
#include <iostream>

class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;
};

class Circle : public Shape {
private:
    double r_;
public:
    Circle(double radius) : r_(radius) {}
    
    double area() const override {
        return 3.14 * r_ * r_;
    }
    
};

class Rectangle : public Shape {
private:
    double w_, h_;
public:
    Rectangle(double width, double height) : w_(width), h_(height) {}
    
    double area() const override {
        return w_ * h_;
    }
    

};

int main() {

    std::vector<polymorphic<Shape>> vecShapes;
    
    vecShapes.push_back(Circle(3.0));
    vecShapes.push_back(Rectangle(2.2, 3.3));
    vecShapes.push_back(Circle(3.5));
    
    for (const auto& s : vecShapes) {
        s->area(); 
    }
    
    
    return 0;
}

说明:代码仅供参考,无法保证可正常运行。

五、总结

在标准的学习中,一定要掌握其类似的库应用,比如本文介绍的两个类模板与variant的不同,二者与智能指针的不同,等等。重点是掌握它们的侧重点在哪里,它们如何能够互相完善C++中的各种场景的实现。就如同辨别孪生兄弟一样,只有掌握好细节的不同,才能更好的展开对技术的应用。

<think>我们正在讨论如何解决使用readelf命令时出现的文件不存在错误,并理解与DW_AT_name相关的调试信息输出。用户之前的问题是关于清除GDB手动指定路径,现在转向了readelf调试信息。根据引用内容:引用[1]:提到了GDB查找debug文件的路径,如果通过debug_link指定了文件名(如main.debug),GDB会在三个特定路径下寻找。引用[2]:解释了ELF文件中调试信息的存储位置,调试信息存储在.debug开头的节中(如.debug_info),这些节包含DWARF格式的调试数据。引用[3]:提供了readelf命令的文档,特别是--debug-dump选项可以用于显示调试信息。用户当前的查询有两个部分:1.解决readelf命令中指定的文件不存在的问题。2.理解DW_AT_name相关的调试信息输出。我们首先分析第一个问题:readelf命令文件不存在错误。可能的原因:-文件路径错误:用户指定的文件路径不正确。-文件没有调试信息:文件不是ELF格式或者没有包含调试信息(即没有.debug_*节)。-readelf命令版本或使用方式问题。对于第二个问题:理解DW_AT_name。在DWARF调试信息中,DW_AT_name是一个属性,用于指定名称,比如一个编译单元(DW_TAG_compile_unit)的源文件名,或者一个子程序(DW_TAG_subprogram)的函数名等。如何解决readelf的文件不存在错误?假设用户使用的命令类似于:readelf--debug-dump=info<filename>错误可能是由于文件不存在或路径错误。建议用户:1.检查文件路径是否正确:使用绝对路径或确保相对路径正确。2.确认文件是否存在:使用ls命令检查。3.确认文件是包含调试信息的ELF文件:使用file命令检查文件类型,并使用readelf-S查看是否有.debug_info等节。如何查看调试信息中的DW_AT_name?使用readelf的--debug-dump选项。例如:readelf--debug-dump=info<filename>这将显示所有调试信息(在.debug_info节中)。在输出中,每个DIE(调试信息项)都可以有一个DW_AT_name属性。例如,一个编译单元的DIE可能如下:<0><b>:AbbrevNumber:1(DW_TAG_compile_unit)<c>DW_AT_producer:(indirectstring,offset:0x0):GNUC119.3.0-mtune=generic-march=x86-64-g<10>DW_AT_language:12(ANSIC99)<11>DW_AT_name:(indirectstring,offset:0x13):main.c<15>DW_AT_comp_dir:(indirectstring,offset:0x1c):/home/user/test这里DW_AT_name的值是源文件名(main.c)。但是,如果文件确实存在但readelf报告错误,可能是由于readelf无法识别该文件格式(例如,非ELF文件),或者文件损坏。另外,如果文件是ELF文件但不包含调试信息,那么使用--debug-dump选项将不会输出任何调试信息(但不会报文件不存在错误)。因此,步骤:第一步:检查文件是否存在$ls-l<filename>第二步:检查文件类型$file<filename>预期输出:ELF64-bitLSBsharedobject,x86-64,version1(SYSV),dynamicallylinked,...第三步:检查是否包含调试信息$readelf-S<filename>|grepdebug如果看到.debug_info等节,则表示有调试信息。第四步:使用readelf输出调试信息$readelf--debug-dump=info<filename>如果以上步骤中在第三步没有发现调试节,则说明该文件没有调试信息,因此使用--debug-dump会找不到调试信息,但不会报文件不存在的错误。那么用户为什么会遇到“文件不存在”的错误?可能是:-在readelf命令中写错了文件名(拼写错误或路径错误)。-用户可能误认为调试信息文件(如main.debug)是单独存在的,而实际上调试信息已经被嵌入到可执行文件中(或者根本没有生成)。根据引用[1],如果可执行文件中有一个.debug_link节,那么GDB会自动查找单独的调试信息文件。但是readelf命令并不会自动查找这些单独的调试信息文件。当使用readelf打开可执行文件时,它只会读取该文件本身,不会去加载外部调试文件。因此,如果调试信息是分开的(在单独的debug文件中),那么用户需要明确指定那个单独的debug文件给readelf,而不是原始的可执行文件。所以,如果用户的可执行文件有一个.debug_link节,那么用户应该:1.找到单独的调试信息文件(在引用[1]中提到的路径中查找,如/usr/bin/main.debug等)。2.然后对单独的调试信息文件运行readelf命令。如何查看.debug_link节?readelf-x.debug_link<filename>或者objdump-s-j.debug_link<filename>.debug_link节的内容是调试信息文件的文件名(如main.debug)一个4字节的CRC校验值。总结解决步骤:情况1:调试信息在可执行文件内部(最常见的情况)-直接对可执行文件使用readelf命令。情况2:调试信息在外部文件-从.debug_link节中获取外部调试信息文件的名称。-然后按照引用[1]中的路径查找该文件,并对其使用readelf命令。如果用户尝试对可执行文件运行readelf--debug-dump,而调试信息在外部文件中,那么readelf会输出可执行文件内部包含的调试信息(可能只有很少的信息,比如只有.debug_link节,而没有完整的调试信息节)。所以用户需要对外部调试文件进行操作。现在,关于DW_AT_name:在调试信息中,DW_AT_name属性出现在多种DIE中,用于标识名称。使用如下命令:readelf--debug-dump=info<debugfile>输出中会显示DIE树,我们可以搜索DW_AT_name。如果用户想查看特定类型的DW_AT_name(如函数名),可以配合其他工具如grep过滤:readelf--debug-dump=info<debugfile>|grep-A5DW_TAG_subprogram|grepDW_AT_name但是,注意:DW_AT_name的值可能是直接字符串,也可能是间接的(通过字符串表引用)。间接引用时,会显示(间接字符串,偏移:0xXX),然后需要查看字符串表(.debug_str节)。readelf在输出时会自动解析这些间接引用,所以输出中通常已经显示了实际的字符串。最后,回答结构:1.解释readelf文件不存在错误的原因及解决方法。2.解释如何查看理解DW_AT_name。生成相关问题:相关问题:1.如何检查ELF文件是否包含调试信息?2.如何生成单独的调试信息文件?3.调试信息中的DWARF格式包含哪些常用属性?4.如何通过readelf查看特定的调试信息(如行号信息)?注意:按照用户要求,回答使用中文,公式用$...$,独立公式用$$...$$(尽管这个问题可能不涉及公式,但如果有数学表达式则遵循),引用标记[^n]。</think> ### 解决readelf文件不存在错误及理解DW_AT_name调试信息 #### 一、解决readelf命令文件不存在错误 当使用`readelf --debug-dump=info`时出现文件不存在的错误,通常有四种原因: 1. **文件路径错误** - 检查文件路径是否拼写错误或包含特殊字符 - 使用绝对路径替代相对路径: ```bash readelf --debug-dump=info /绝对路径/可执行文件 ``` 2. **调试信息分离存储(最常见原因)** 根据引用[1],GDB会通过`.debug_link`查找分离存储的调试文件。解决方法: - 检查可执行文件是否包含`.debug_link`节: ```bash readelf -x .debug_link 可执行文件 ``` - 在以下位置手动查找调试文件(假设`.debug_link`指向`main.debug`): ```bash /usr/bin/main.debug /usr/bin/.debug/main.debug /usr/lib/debug/usr/bin/main.debug ``` 3. **未生成调试信息** 验证是否已用`-g`选项编译: ```bash gcc -g main.c -o program # 确保包含调试信息 ``` 4. **文件非ELF格式** 使用`file`命令验证文件格式: ```bash file 可疑文件 # 确认输出是否包含"ELF" ``` #### 二、理解DW_AT_name调试信息 DW_AT_name是DWARF调试格式的关键属性,其作用查看方法如下: ##### 1. DW_AT_name的作用 - **定义**:DIE(调试信息条目)的名称标识符 - **核心用途**: - 源代码文件名(在`DW_TAG_compile_unit`中) - 函数名(在`DW_TAG_subprogram`中) - 变量名/类型名(在`DW_TAG_variable`/`DW_TAG_typedef`中) - **存储位置**: - 直接在`.debug_info`节中存储短字符串 - 长字符串存储在`.debug_str`节,通过偏移量引用[^2] ##### 2. 查看DW_AT_name的方法 ```bash readelf --debug-dump=info 可执行文件 | grep -A5 DW_AT_name ``` **典型输出示例**: ``` <2d>: DW_AT_name : "main" (函数名) <5a>: DW_AT_name : "data.c" (源文件名) <8f>: DW_AT_name : "counter" (变量名) ``` ##### 3. 调试信息完整路径解析 若遇到路径不完整(如仅有文件名无路径),通过`DW_AT_comp_dir`重建路径: ```bash readelf --debug-dump=info 可执行文件 | grep -e DW_AT_name -e DW_AT_comp_dir ``` 输出组合: `DW_AT_comp_dir: /home/user/project` + `DW_AT_name: src/main.c` ⇒ 完整路径:`/home/user/project/src/main.c` #### 三、调试信息问题排查流程 ```mermaid graph TD A[readelf报错] --> B{文件是否存在} B --> |否| C[检查路径拼写] B --> |是| D{是否为ELF文件} D --> |否| E[重新编译] D --> |是| F{是否有.debug_info} F --> |无| G[添加-g选项编译] F --> |有| H{是否有.debug_link} H --> |有| I[查找分离调试文件] H --> |无| J[分析DWARF属性] ``` #### 四、GDB与调试信息的关联 当调试信息完整时,GDB可直接显示源码位置: ```gdb (gdb) break main Breakpoint 1 at 0x40057a: file src/main.c, line 5. ``` 对应DWARF信息: - `DW_TAG_subprogram`: DW_AT_name="main" - `DW_TAG_compile_unit`: DW_AT_name="main.c" 若出现源文件路径错误,参考[清除GDB路径的方法](原回答链接)重置设置。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值