首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

为什么我们不能得到一个指向泛型结构的指针?

在C++中,泛型结构是通过模板来实现的,它可以根据不同的数据类型生成不同的结构。然而,由于泛型结构的实例化是在编译时完成的,而指针的类型是在运行时确定的,因此我们不能直接得到一个指向泛型结构的指针。

当我们定义一个泛型结构时,编译器会根据使用该结构时传入的具体类型生成对应的代码。这意味着每个具体类型都会生成一个独立的结构,它们在内存中的布局和大小可能是不同的。因此,如果我们尝试将一个指向泛型结构的指针赋值给一个指针变量,编译器无法确定该指针变量应该指向哪个具体类型的结构,从而导致编译错误。

为了解决这个问题,我们可以使用模板特化来实现指向泛型结构的指针。模板特化是指为特定的数据类型提供特定的实现。通过为每个具体类型提供特定的结构定义和相关操作,我们可以在编译时确定指针的类型,并使用指向特定类型结构的指针。

总结起来,我们不能直接得到一个指向泛型结构的指针,因为泛型结构的实例化是在编译时完成的,而指针的类型是在运行时确定的。要实现指向泛型结构的指针,可以使用模板特化来为特定的数据类型提供特定的实现。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

c语言链表指向下一个结构体指针,结构体和它的众多小细节

所以我么就可以定义一个学生的结构体,里面包含了他的各种属性,只是需要注意我们只是定义了一种数据类型,如果要向内存申请存储单元还要继续声明变量。...在这里还有一个小细节,就是对于字符串的处理。字符串不能被赋值,只能采用字符数组或者字符拷贝函数strcpy()等方式处理。字符串的名字表示首地址,是地址常量,常量不能被赋值。...我们在之前提到,想对某一段一段内存进行操作的前提是【把该变量表示出来】。 对于结构体指针,可以望名知意:这是一个指针,只不过这个指针里面存放的地址是一个结构体变量的地址。...对结构体指针而言,访问它所指向的结构变量的成员可以采用取值运算符*,比如struct (*stu).name。当然,我们在实践中更喜欢采用的方式是箭头方式:struct stu->name。...只是对于初学者而言,可能很难理解为结构体指针类型起别名的方式。这里只需把它当作一种等价替换就可以,为结构体指针起别名之后会把指针标志*给藏起来,但是在实际使用中要时刻注意,这仍旧是一个指针。

1.2K21

泛型和元编程的模型:Java, Go, Rust, Swift, D等

基本想法 假设我们用一种没有泛型系统的语言进行编程,我们想实现一个通用的堆栈数据结构,它对任何数据类型都有效。...这种方法叫做 "vtables"(由 "虚拟方法表 "缩写而来),它的实现方式是,在通用结构中的每个对象的偏移量为0的地方,都有一个指向函数指针表的指针。...当你把一个类型转换为一个接口类型时,它会创建一个包装器,这个包装器包含一个指向原始对象的指针和一个指向该接口特定类型函数的vtable的指针。...动态类型语言 反射是非常强大的,可以完成很多不同的元编程任务,但有一点它不能做,那就是创建新的类型或编辑现有字段的类型信息。如果我们增加了这样的能力,并通过反射来实现,最终就会得到动态类型语言。...,这也是为什么Rust可以使用同一个类型系统来支持这两种泛型的原因!

3.1K30
  • 泛型会让你的 Go 代码运行变慢

    整个过程跟设计文档的说明完全相符:用于传递指向结构的 stenciling 过程会将指针单态化为类似 void 的指针。单态化期间不考虑指向对象的其他属性,因此无法进行内联。...这样,函数调用的第一个参数就必须是 buf.(*face).data,即指向我们接口内 strings.Builder 的实际指针。...出于这一现实,stenciling 实现才需要向每一个泛型函数调用传递字典:字典中包含的,就是指向函数所有泛型参数的 itab 的指针。 说到这里,大家应该理解为什么我们的程序集要费力使用字典了。...这就是我们从分析中得到的第一个结论:在 1.18 中,我们没必要将带有接口的纯函数转换成泛型函数,因为 Go 编译器目前无法生成通过指针调用方法的函数 shape,所以转换只会拖慢代码运行速度。...所以,我们得到一个明确的结论:千万别把接口传递给 Go 中的泛型函数。即使在最理想的情况下,即接口与约束完全匹配时,指向类型的每一次方法调用都会产生大量开销。

    1.1K20

    泛型会让你的 Go 代码运行变慢

    整个过程跟设计文档的说明完全相符:用于传递指向结构的 stenciling 过程会将指针单态化为类似 void 的指针。单态化期间不考虑指向对象的其他属性,因此无法进行内联。...这样,函数调用的第一个参数就必须是 buf.(*face).data,即指向我们接口内 strings.Builder 的实际指针。...出于这一现实,stenciling 实现才需要向每一个泛型函数调用传递字典:字典中包含的,就是指向函数所有泛型参数的 itab 的指针。 说到这里,大家应该理解为什么我们的程序集要费力使用字典了。...这就是我们从分析中得到的第一个结论:在 1.18 中,我们没必要将带有接口的纯函数转换成泛型函数,因为 Go 编译器目前无法生成通过指针调用方法的函数 shape,所以转换只会拖慢代码运行速度。...所以,我们得到一个明确的结论:千万别把接口传递给 Go 中的泛型函数。即使在最理想的情况下,即接口与约束完全匹配时,指向类型的每一次方法调用都会产生大量开销。

    1.2K40

    Golang 泛型实现原理

    泛型提供了一种更灵活、更通用的方式来编写函数和数据结构,以处理不同类型的数据,而不必针对每种类型编写重复的代码。 1.有 interface{} 为什么还要有泛型?...以下是 Go 泛型实现的基本原理: 2.1 类型参数 Go 的泛型使用类型参数来实现通用性。在定义函数、数据结构或方法时,可以声明一个或多个类型参数。...在函数体内,可以使用 T 来表示参数和返回值的类型。 泛型数据结构 泛型也可以用于创建通用的数据结构,如泛型切片、泛型映射等。这样可以更灵活地处理不同类型的数据。...这样做是因为指针看起来总是一样的,不管它指向的是什么类型。 如果这些值是对象,而泛型函数需要调用这些对象的方法,它就不能再这样做了。该函数只有一个指向对象的指针,不知道它们的方法在哪里。...参考wenxian An Introduction To Generics 泛型设计 - | Go 语言设计哲学- 煎鱼 golang拾遗:为什么我们需要泛型- apocelipes 简单易懂的

    64210

    【笔记】C++标准库: 体系结构与内核分析(上)

    只朝尾部单向扩充, 不能添加元素在头部, 通常用push_back()压入数据 序列型 list 双向链表 1. 每次增长只扩充一个节点 2....对于模板编程, 有一个很实用的设计在STL中非常常见: 特化(Specialization), 相对普通的模板编程叫做泛化, 特化又可以细分为全特化和偏特化, 这种写法使得我们能保留泛化函数的通用性的情况下...它们分别是: value_type: 迭代器所指向的数据的实际类型, 通常是T difference_type: 迭代器做差得到的类型(索引距离), 通常是 ptrdiff_t pointer: 迭代器所指向的数据的指针类型...()需要random_access_iterator_tag型的迭代器 萃取器Traits 算法本来可以直接访问迭代器等类来得知所需的额外信息, 但是为了泛用性, 我们也希望算法可以直接应用在原生指针上...由于rb_tree属于一种排序二叉树, 所以按照正确规则进行遍历的话树中的节点将以排序顺序得到. rb_tree结构只有一个指向header节点的指针和记录节点数量的值.

    1.2K30

    为什么泛型会让你的Go程序变慢

    inteface 是一个 16 bytes 的胖指针实现的,结构体名 iface, 第一个指针指向接口的元数据信息 itab, 第二个指针指向值本身 type iface struct { tab *...WriteByte 方法,我们需要指向 itab 的指针。...) 如果你还记得,这就是为什么 go 所谓的模版化实现(stenciling), 要给每个泛型函数调用传递一个字典 dictionary 的全部原因:这个字典包含指向函数的所有泛型参数的 itab 的指针...但令人惊讶的是:泛型函数也是 3 allocs/op, 尽管生成的函数实例化直接使用了指针,但 escape analysis 不能再证明它是 non-escape, 所以我们得到了一个额外的堆分配。...现在它可以做一些非常强大的事情,当泛型不碍事的时候 让我给你举个例子:想象一下我们正在开发一个库,为 Go 增加函数式调用。我们为什么要这样做呢?我也不知道。很多人似乎都在做这件事。

    35230

    C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)

    在释放变量的时候,其顺序总是与给它们分配内存的顺序相反,后进先出,这就是堆栈的工作方式。 堆栈是向下填充的,即从高地址向低地址填充。当数据入栈后,堆栈指针就会随之调整,指向下一个自由空间。...我们来举个例子说明。 ? 如图,假如堆栈指针2000,下一个自由空间是1999。下面的代码会告诉编译器需要一些存储单元来存储一个整数和一个双精度浮点数。...此时,堆栈指针就减4,指向新的已用空间的末尾1996,下一个自由空间为1995。下一行声明d赋值3.5后,double需要占用8个字节,所以存储在1988~1995上,堆栈指针减去8。   ...变量的生存期总是嵌套的,当d在作用域的时候,无论发生什么事情,都可以保证堆栈指针一直指向存储d的空间。删除这个d变量的时候堆栈指针递增8,现在指向d曾经使用过的空间,此处就是放置闭合花括号的地方。...可以看出泛型可以避免装箱拆箱带来的不必要的性能消耗;当然泛型的好处不止于此,泛型还可以增加程序的可读性,使程序更容易被复用等等,至于泛型以后再做详细介绍。

    1.2K41

    C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)

    在释放变量的时候,其顺序总是与给它们分配内存的顺序相反,后进先出,这就是堆栈的工作方式。 堆栈是向下填充的,即从高地址向低地址填充。当数据入栈后,堆栈指针就会随之调整,指向下一个自由空间。...我们来举个例子说明。 ? 如图,假如堆栈指针2000,下一个自由空间是1999。下面的代码会告诉编译器需要一些存储单元来存储一个整数和一个双精度浮点数。...此时,堆栈指针就减4,指向新的已用空间的末尾1996,下一个自由空间为1995。下一行声明d赋值3.5后,double需要占用8个字节,所以存储在1988~1995上,堆栈指针减去8。   ...变量的生存期总是嵌套的,当d在作用域的时候,无论发生什么事情,都可以保证堆栈指针一直指向存储d的空间。删除这个d变量的时候堆栈指针递增8,现在指向d曾经使用过的空间,此处就是放置闭合花括号的地方。...可以看出泛型可以避免装箱拆箱带来的不必要的性能消耗;当然泛型的好处不止于此,泛型还可以增加程序的可读性,使程序更容易被复用等等,至于泛型以后再做详细介绍。

    1.1K10

    《Rust for Rustaceans》 样章试译 | 第二章 Rust 基础

    我们可以通过解引用(dereference)指针来访问存储在它所指向内存位置的值。也可以在多个变量中存储相同的指针,这些变量正确地指向内存中的同一个位置,从而指向相同的值。...但你不能改变所指向的值(即 x 的值)。同样,你可以通过z来改变y的指针值,但你不能改变 z 自身,使其指向一个不同的值。...那么,当涉及到生存期时候,为什么需要学习型变呢?当你考虑泛型生存期如何与借用检查器交互时,型变就变得相关了。考虑清单2-11中所示类型,它在一个字段中使用了多个生存期。...("{}", s); // 清单 2-11: 需要多个泛型生存期的类型 乍一看,在这里使用两个生存期似乎没必要,我们没有任何方法需要区分结构中不同部分的借用,就像清单2-10中的StrSplit那样...我们将讨论类型如何在内存中表示,看看泛型和特质(trait)如何产生执行代码,并看看 Rust 为更高级的用例提供的一些特殊类型和特质结构。

    5.9K31

    Golang interface 接口详细原理和使用技巧

    任何一个 interface{} 类型的变量都包含了2个指针,一个指针指向值的类型,对应 pair 中的 type,这个 type 类型包括静态的类型 (static type,比如 int、string......)和具体的类型(concrete type,interface 所指向的具体类型),另外一个指针指向实际的值,对应 pair 中的 value。...主要原因有如下几点: 可以实现泛型编程(虽然 Go 在 1.18 之后已经支持泛型了) 在 C++ 等高级语言中使用泛型编程非常的简单,但是 Go 在 1.18 版本之前,是不支持泛型的,而通过 Go...使用的时候不管数组的元素类型是什么类型(int, float, string…),只要我们实现了这三个方法就可以使用 Sort 函数,这样就实现了“泛型编程”。...因为一个 interface{} 类型的变量包含了2个指针,一个指针指向值的类型,另外一个指针指向实际的值。

    1.5K20

    C语言(指针)2

    我们接着调试看一下结果: 好像跟我们想的不一样,执行完 *pa = 0;这条语句后只是把最小的地址(一个内存单元)中的值该为了0。这是为什么呢?...3.2指针+-整数 观察下面的代码: 跟我们想的一样,&a、pa、pc的值是一样的,但当我们给&a、pa、pc加一个整数1的时候得到了不一样的结果,通过观察,&a和pa...3.3void *类型 在指针类型中有一种特殊的类型是 void * 类型的,为无具体类型的指针(泛型指针),这种类型的指针可以用来接收任意类型的地址。...一般void *类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果,使得一个函数来处理多种类型的数据。在后面的文章中会深入探讨。...当变量a被const “训练” 过后,我们再试图去改变它的值,就会发现编译器报错,说a是不可被修改的,变量a得到了 “强化”。 但是const修饰的变量本质上还是变量,只是不能被修改。

    9010

    C语言——指针(1)

    所以指针的类型决定了指针+-1时跳过多少个字节。 在指针中有一种特殊类型的指针,void*类型的指针,也叫做泛型指针。 1.void*指针可以⽤来接受任意类型地址。...2.void*指针不能直接进行指针的+-整数和解引⽤的运算。 void*指针一般用于函数参数部分,接收不同类型的地址,实现泛型编程的效果。这样一个函数就能处理多种类型的数据。...五.const修饰指针 1.const如果放在*的左边,修饰指针指向的内容,指针指向的内容不能通过指针来改变。但是指针本身的内容可变。...打印数组: 指针减指针得到的是两个指针指向内存之间元素的个数。 模拟实现strlen函数: 指针的关系运算:指针与指针比较大小。 可以用下面的例子辅助了解。...对于指针指向的空间释放,我们看下面这个关于函数的例子。 上面的p指针就是野指针,其指向的内存已经释放。为什么?

    7910

    【C++】list的使用和基本迭代器框架的实现 & vs和g++下string结构的说明

    ,是一个自定义类型,并非原生指针的内置类型,所以解引用迭代器我们拿到的是结构体对象,而并非是数据内容,这就不符合迭代器的特征,因为迭代器的本意就是要解引用拿到数据,而我们拿到的是一个结构体对象,这就有问题了...用一个结点的指针就可以作为list迭代器的成员变量了,迭代器本质就是一个对象,这个对象的成员变量是结构体指针,通过迭代器类和迭代器对象我们才能让list的迭代器实现解引用加加减减等操作。 5....* _prev;//指向前一个结点的结构体指针 T _data;//数据类型是泛型,可能是内置类型,也有可能是自定义类型 list_node(const T& x) //new结点的时候会调用构造函数...,new会自动调用node类的带参构造函数,我们给构造函数传一个泛型的匿名对象, //保证结点存储的数据类型是泛型,既有可能是内置类型也有可能是自定义类型,所以传匿名对象。...下面所说的默认环境是32位平台,指针为4字节。从打印结果我们可以得到两个信息,一个是s1和s2的所占字节大小一样,另一个是两者所占字节大小为28字节。

    50610

    为什么指针被誉为 C 语言灵魂?

    对,就是指针,你可以这样: int *pa = &a; pa 中存储的就是变量 a 的地址,也叫做指向 a 的指针。 在这里我想谈几个看起来有点无聊的话题: 为什么我们需要指针?...不管几级指针有两个最核心的东西: 指针本身也是一个变量,需要内存去存储,指针也有自己的地址 指针内存存储的是它所指向变量的地址 这就是我为什么多级指针是逻辑上的概念,实际上一块内存要么放实际内容,要么放其它变量地址...下面的例子就 是一个 void 指针: void *ptr; void 指针最大的用处就是在 C 语言中实现泛型编程,因为任何指针都可以被赋给 void 指针,void 指针也可以被转换回原来的指针类型...不过也有需要注意的: 不能对 void 指针解引用 比如: int num; void *pv = (void*)# *pv = 4; // 错误 为什么?...但是 void,编译器是不知道它到底指向的是 int、double、或者是一个结构体,所以编译器没法对 void 型指针解引用。

    73410

    坚持还是放弃,Go语言的“美好与丑陋”解读

    由于内置集合(map,slice 和 array)是引用并且是可变的,所以复制包含其中之一的结构体只会将复制指向同一后台内存的指针。 下面的示例说明这一点: ?...对 Bang 的调用成功了,因为它应用在指向 Bomb 的指针上:不需要解引用该指针来调用该方法。Boom 方法操作一个值,因此一个调用导致指针被解引用,这会导致 panic。...没有泛型......至少不适合你 很难想象一个没有泛型的现代静态类型语言,但这就是你用 Go 得到的东西:它没有泛型......或者更确切地说几乎没有泛型,正如我们将看到的那样,这使得它比没有泛型更糟糕...然而,没有用户可定义的泛型数据结构。这意味着你无法以类型安全的方式定义可用于任何一个 type 的可复用 abstractions。...正如接下来我们将看到的,把内置的泛型与用户定义的非泛型分隔开,对开发者的“舒适度”和编译时的类型安全产生了影响:它影响了整个Go的生态系统。

    1.7K41

    透过 Rust 探索系统的本原:泛型

    他说,目前我们走了三步: 第一步,通用的计算机体系结构:将内存视作一组连续可寻址的空间 第二步,通用的计算机语言:使用指针作为统一的引用类型的标识符 第三步,泛型编程 今天我们就来讲讲泛型编程。...这会生成一个 Trait Object,在上图中,我们可以看到,Trait Object 的底层逻辑不过就是胖指针(fat pointer) —— 一个包含两个指针的数据结构。...其中,一个指针指向数据本身,另一个则指向虚函数表(vtable)。...当然,C++/Java 指向 vtable 的指针在编译时放在类结构里,而 Rust 放在 Trait object 中。...支持泛型的语言并不能帮助你更好地做泛型编程,就好比给我一台斯坦威钢琴,并不意味着我就具备了演奏李斯特《钟》的能力。

    1.2K40

    程序员C语言快速上手——高级篇(十)

    例如将数组声明为全局数组变量,那么就必须在声明时静态指定数组的长度。假如我们用一个数组来存放会员注册信息,那么我们根本不能在编译时确定数组的具体长度,显然的,我们需要一个可以动态增长的内存区域。...我们知道数组变量实际上也是一个指针,指向数组的起始地址,结构体指针也是指向第一个成员变量的起始地址,而函数指针亦是指向函数的起始地址。 所谓函数指针,就是一个保存了函数的起始地址的指针变量。...首先思考一个问题,指针仅仅是用来保存一个内存地址的,所有的内存地址都只是一个号码,那么指针为什么还需要类型呢?理论上所有的指针都应该是同一种类型才对呀?...接触过Java等具有泛型的面向对象编程语言的人,可能马上就会联想到泛型,是的,C语言没有泛型,但是利用void*指针的特点,我们可以使用一些技巧来模拟泛型编程。...像Java这样的编程语言存在泛型,我们可以定义泛型,而不需要在函数声明时指定具体类型,当调用的时候传入的是什么类型,函数就计算什么类型,我们看一下C语言如何实现 // 交换两个变量的值 void swap

    1.4K30

    模板进阶:特化与编译链接全解析

    5个元素的数组对象 模板的特化 为什么要有模板的特化 模板技术提供了强大的泛型编程能力,使得我们能够编写与数据类型无关的代码,从而提高代码的复用性和灵活性。...private: int first_; T2 second_; }; 在这个部分特化版本中,我们特化了Pair模板的第一个类型为int,第二个类型保持泛型。...const Date* pDate; 在这个例子中,pDate是一个指向Date对象的指针。虽然pDate本身可以指向不同的Date对象,但不能通过pDate来修改它所指向的对象的内容。...换句话说,指针本身的地址不能改变,也就是说,一旦初始化后,指针不能指向其他地址,也就是传入的指针不能被修改了,和通用模板实现的效果相同。...因此,Date* const& 的意思是“指向Date对象的常量指针的引用”。这个引用在函数内不会改变其所引用的指针对象,也不能通过引用修改指针本身的指向。 已经特化的类中T表示为什么?

    17810

    Python源码剖析:深度探索Cpython对象-达观数据

    在 Python 中创建一个对象,会分配内存并进行初始化,然后 Python 会用一个 PyObject * 来保存和维护这个对象,因此在 Python 中,变量的传递(包括函数的参数传递)实际上传递的都是一个泛型指针...这个指针具体指向什么类型的对象我们并不知道,只能通过其内部的 ob_type 成员进行动态判断,而正是因为这个 ob_type,Python 实现了多态机制。...因为我们说 Python 中的变量都是一个 PyObject *,所以它可以指向任意的对象,因此 Python 就无法做基于类型方面的优化。...事实上,Python 内部创建一个对象的方法有两种:• 通过 Python/C API,可以是泛型API、也可以是特型API,用于内置类型• 通过对应的类型对象去创建,多用于自定义类型Python 对外提供了...以 f = 3.14 为例,底层结构如下:使用泛型 API 创建使用特型 API 创建综上,不管采用哪种方式创建,最终的关键步骤都是分配内存,创建内置类型的实例对象,Python 是可以直接分配内存的。

    30010
    领券