这个函数模板在这个类模板中做什么

What is this function template in this class template doing?

本文关键字:什么 函数模板      更新时间:2023-10-16

我正在阅读一本书中的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不总是指向数组中的第一个元素,因为它只是在前一行上创建的?!在这种上下文中,++操作符的目的是什么?

让我们分解它的工作原理:

  1. 这是函数声明。重要的一点是PT无关。因此,我们需要多加注意,因为一些不好的事情可能会发生……

    template <typename P> 
    P* Write(P* pData)
    {
    

    表示pData所指向的内存。为了便于解释,我将假设pData指向内存中足够大的区域(否则Write可能会导致段故障),并且Vector3<T>只有3个T的大小。在下面,我将使用T = floatsizeof(float) = 4,但我的解释对于其他类型仍然有效。这里是sizeof(Vector3<float>) = 12 .

    所以,这里是内存,|-|是一个字节:

    |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
    ^
    |
    pData : P*
    
  2. 我们将pData解释为指向Vector3<T>的指针。这是一个糟糕的风格一般,因为P可以是任何。

        Vector3<T>* pVector = (Vector3<T>*) pData; 
    
  3. 下一行:

        *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|-|-|-|-|-|
      
  4. 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;
}

它更容易理解,维护,调试等。