这个函数模板在这个类模板中做什么
What is this function template in this class template doing?
我正在阅读一本书中的c++类模板,虽然大部分内容都很清楚,但这个特殊的函数确实让我很困扰:
template <typename T>
struct Vector3 {
T x; T y; T z;
//... several methods, constructors, etc
//Then this one, which is really confusing me:
template <typename P>
P* Write(P* pData)
{
Vector3<T>* pVector = (Vector3<T>*) pData;
*pVector++ = *this;
return (P*) pVector;
}
首先,这个函数似乎在处理p数组或指向p的指针,就好像它可以很容易地从指向Vector3
(类名)的指针强制转换一样。这是怎么回事?如果我有一个Vector3<float> someVector
,是什么使得指向someVector
的指针可以转换成指向float
的指针?(甚至int
?)它也做相反的事情:给函数提供一个浮点数数组,然后它可以将其强制转换为Vector3
数组。
所以这是我困惑的第一个地方。
接下来是*pVector++ = *this;
——我认为这是这里的指针算术,如果pVector
指向数组中的第二个元素,使用这个表达式,我们将指针递增到数组的下一个元素,但只有在之后,我们首先将*this
赋值给pVector
指向的当前元素。假设我在这一点上是正确的,难道pVector
不总是指向数组中的第一个元素,因为它只是在前一行上创建的?!在这种上下文中,++
操作符的目的是什么?
让我们分解它的工作原理:
-
这是函数声明。重要的一点是
P
与T
无关。因此,我们需要多加注意,因为一些不好的事情可能会发生……template <typename P> P* Write(P* pData) {
表示
pData
所指向的内存。为了便于解释,我将假设pData
指向内存中足够大的区域(否则Write
可能会导致段故障),并且Vector3<T>
只有3个T
的大小。在下面,我将使用T = float
和sizeof(float) = 4
,但我的解释对于其他类型仍然有效。这里是sizeof(Vector3<float>) = 12
.所以,这里是内存,
|-|
是一个字节:|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| ^ | pData : P*
-
我们将
pData
解释为指向Vector3<T>
的指针。这是一个糟糕的风格一般,因为P
可以是任何。Vector3<T>* pVector = (Vector3<T>*) pData;
-
下一行:
*pVector++ = *this;
可分为:
*pVector = *this; pVector++;
*pVector = *this;
将向量(*this
)的内容赋值给数据。当前内存为:pVector : Vector3<float>* | v |X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-| ^^^^^^^^^^^^^^^^^^^^^^^^^ copied content of the vector
pVector++;
将向量增加1 * sizeof(Vector3<float>)
,因此它现在指向尚未写入的内存:pVector : Vector3<float>* | v |X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-|
-
pVector
被强制转换回P*
并返回。return (P*) pVector; }
允许链式写操作,因为对返回指针的写操作不会覆盖第一次写操作:
char data[2 * sizeof(Vector3<float>)]; v2.Write(v1.Write(data)); // now data is: // contents of v2 // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // |X1|X1|X1|X1|Y1|Y1|Y1|Y1|Z1|Z1|Z1|Z1|X2|X2|X2|X2|Y2|Y2|Y2|Y2|Z2|Z2|Z2|Z2 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // contents of v1
现在,一些缺陷:
此函数可用于io,相当于vector上的memcpy
。然而,它有很多缺陷,最主要的是模板参数。对于这样的操作,直接写入内存应该使用char
,而不是其他东西。在内存中写入以覆盖类的内容(即,不是io)是一个非常糟糕的实践,(赋值操作符是为这样的任务而设计的,它们更安全)。同样,下面的例子不是自描述的:
vec.Write<MyTimerClass>(pointer); // Does not make sense if you are reading this line for the first time
其次,复制内存存在问题。在这里,在解释中,我假设Vector3
是一个简单的类型。情况并非总是如此。如果它具有不同大小的虚函数和成员,则内存中的布局应该是由实现定义的,例如:
X, Y, Z padding
------------------------ ----
|P|P|P|P|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|a|a|.|.
-------- ----
vtable some
pointer other
member
这对于io是不可靠的。看看这个SO问题(公认的答案是:"这是它如何为msvc工作的"…)。对于多重虚拟继承来说,这是一个真正的噩梦。
顺便说一下,这种方法的安全实现应该类似于:template<typename IOBuffer>
bool Write(IOBuffer& buffer)
{
return buffer << X << Y << Z;
}
或更好:
virtual bool Write(IOBufferInterface& buffer)
{
return buffer << X << Y << Z;
}
它更容易理解,维护,调试等。
- 当函数模板参数是具有默认参数的类模板时,函数模板参数的推导如何执行
- 将重载的成员函数传递给函数模板
- C++17中函数模板中的静态数组初始化(MSVC 2019)
- 为什么 gcc 和 clang 为函数模板的实例化生成不同的符号名称?
- 具有常量引用参数的函数模板专用化
- std::span<const T> 作为函数模板中的参数
- 如何编写一个完美的缩写函数模板?
- 以下代码中的函数模板有什么问题?
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 函数模板实例化、替换和重载解析的顺序是什么?
- AFT(缩写函数模板)有什么争议?
- 函数模板部分专业化-有什么解决方法吗
- 将显式指定的函数模板重载作为模板参数传递的正确语法是什么?
- 关于函数模板中定义的 lambda 闭包类型可以说些什么?
- 声明函数模板的5种新语法是什么
- 关于函数模板的相同代码块在g++下编译正常,但在VC6下编译错误,原因是什么
- 函数模板和委托的区别是什么?
- 创建专门化函数模板的最佳方法是什么?
- 这个函数模板在这个类模板中做什么
- 函数模板的这两种定义有什么区别?