如何用模板函数实现多态?

How to achieve polymorphism with templated functions?

在我的项目中,我有一个带有接口的基本抽象 class,它派生了 classes 实现。这些派生的 classes 具有接受不同类型参数的通用函数。我使用函数模板在派生的 classes 中编写了这些通用函数。

我想将这些模板化功能添加到我的基地 class 的界面中。所以我可以实现多态性:在其他函数中接受基数 class 并在派生的 classes.

中调用这些模板化函数

当我们有普通函数时,我们会做虚拟和覆盖,但是你不能用模板函数做虚拟。

我试图在我的抽象基础中做纯抽象模板函数 class 但它不起作用。

这是一个具有我要实现的功能的小程序,由于 virtual <template...:

而无法编译
#include <vector>

class ObjectTransformerBaseAbstractClass {
public:
    virtual template<typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) = 0;
    virtual template<typename TStructure> std::vector<unsigned char> ToBytes(TStructure structure) = 0;
};

class ObjectTransformer1 : public ObjectTransformerBaseAbstractClass {
    template <typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) {
        // some implementation
    }

    template <typename TStructure> std::vector<unsigned char> ToBytes(TStructure structure) {
        // some implementation
    }
};

class ObjectTransformer2 : public ObjectTransformerBaseAbstractClass {
    template <typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) {
        // some other implementation
    }
    template <typename TStructure>
    std::vector<unsigned char> ToBytes(TStructure structure) {
        // some other implementation
    }
};

template <typename TStructure>
void coutStructureBytes(ObjectTransformerBaseAbstractClass *objectTransformerBaseAbstractClass, TStructure structure) {
    // transform structure to bytes using the passed objectTransformerBaseAbstractClass object and cout it.
}

在我的基础 class 中,我需要说“实现这些纯抽象泛型函数,它们接受不同类型的不同参数并在派生的 classes 中执行操作”。在我派生的 classes 中,我需要实现这些接受不同类型参数的纯抽象泛型函数。

我不明白如何实现我想要的这个功能(你可以在上面的程序中看到,如果它编译并工作的话)。请推荐一个解决方案或解释如何进行这项工作。

您正在尝试使用声明为 virtual 的方法模板。这是不可能的。另请参阅 this answer 相关问题。

看来您正在尝试创建某种 serialization/deserialization 机制。您希望将不同的结构(在不同的 classes 中实现)转换为字节,并希望能够通过以某种方式解释字节来重新创建正确类型的对象。

我们先来处理序列化问题。您可以通过使用每个结构都应实现的序列化方法为所有结构定义一个接口来轻松解决该问题:

class Structure
{
public:
    virtual std::vector<uint8_t> ToBytes() = 0;

    // any other functionality common to all structures
};

一个特定的结构将如下所示:

class SomeStructure: public Structure
{
public:
    SomeStructure(std::vector<uint8_t> bytes)
    {
        // deserialize (create object from bytes)
    }

    std::vector<uint8_t> ToBytes() override
    {
        // serialize
    }

    // explained below
    static constexpr uint8_t TypeId()
    {
        return 42;
    }

    // any specific methods for the structure
};

现在是反序列化问题。您需要有一些方法来识别字节流中的 class 类型。假设您的结构始终写入一个唯一的 class ID 作为第一个字节。然后,您可以使用通过识别类型来创建正确对象的工厂:

class StructureBuilder
{
public:
    virtual Structure* FromBytes(std::vector<uint8_t> bytes) = 0;
    virtual uint8_t TypeId() = 0;
};

class StructureFactory
{
public:
    static StructureFactory& Instance()
    {
        // usual singleton stuff: create if not existing yet, then return instance
    }

    Structure* FromBytes(std::vector<uint8_t> bytes)
    {
        for (auto& builder : builders)
        {
            if (builder->TypeId() == bytes[0])
                return builder->BuildStructureFromBytes(bytes);
                
            return nullptr;
        }
    }

    void Add(StructureBuilder* builder)
    {
        // add to list
    }

    void Remove(StructureBuilder* builder)
    {
        // remove from list
    }

private:
    StructureFactory()
    {
    }
    std::list<StructureBuilder*> builders;
    static StructureFactory* instance;
};

建造者看起来都非常相似,因此您可以使用此处的模板来更轻松地为每个结构创建一个:

template <typename T>
class TypedStructureBuilder: public StructureBuilder
{
public:
    TypedStructureBuilder()
    {
        StructureFactory::Instance().Add(this);
    }

    virtual ~TypedStructureBuilder()
    {
        StructureFactory::Instance().Remove(this);
    }

    Structure* BuildStructureFromBytes(std::vector<uint8_t> bytes) override
    {
        return new T(bytes);
    }

    uint8_t TypeId() override
    {
        return T::TypeId();
    }
};

那么你需要做的就是在工厂注册一些新结构,把它放在某个地方(我会把它放在 SomeStructure.cpp):

static TypedStructureBuilder<SomeStructure> builder;

希望对您有所帮助。

这是我试图实现的行为的 C# 版本:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace baseAbstractTemplates.NET {
    interface IObjectTransformer {
        TStructure ToStructure&lt;TStructure&gt;(ICollection&lt;byte&gt; bytes);
        ICollection&lt;byte&gt; ToBytes&lt;TStructure&gt;(TStructure structure);
    };

    class ObjectTransformer1 : IObjectTransformer {

        #region Implementation of IObjectTransformerBaseAbstractClass

        public TStructure ToStructure&lt;TStructure&gt;(ICollection&lt;byte&gt; bytes) {
            throw new NotImplementedException();
        }

        public ICollection&lt;byte&gt; ToBytes&lt;TStructure&gt;(TStructure structure) {
            throw new NotImplementedException();
        }

        #endregion

    }

    class ObjectTransformer2 : IObjectTransformer {

        #region Implementation of IObjectTransformerBaseAbstractClass

        public TStructure ToStructure&lt;TStructure&gt;(ICollection&lt;byte&gt; bytes) {
            throw new NotImplementedException();
        }

        public ICollection&lt;byte&gt; ToBytes&lt;TStructure&gt;(TStructure structure) {
            throw new NotImplementedException();
        }

        #endregion

    }

    class Program {
        public static void CoutStructureBytes(IObjectTransformer objectTransformer) {
            var bytes = objectTransformer.ToBytes(3);
            Console.WriteLine(bytes);
        }

        static void Main(string[] args) {
            ObjectTransformer1 objectTransformer1 = new ObjectTransformer1();
            ObjectTransformer2 objectTransformer2 = new ObjectTransformer2();
            CoutStructureBytes(objectTransformer1);
            CoutStructureBytes(objectTransformer2);
        }
    }
}

在 C# 中,它仅适用于“哈哈 C# 接口、模板、多态性 go brrr”风格。即使您完全不熟悉 C# 但具有 C++ 知识,我相信您也可以很好地理解 C# 代码。

编译和运行都很好,抛出 NotImplementedException 因为没有实现。

但在 C++ 中,与在 C# 中不同,我不能仅使用常用工具来获得与模板、继承和多态性的接口:纯抽象函数(我在派生 类 中重写)、模板、继承和方法重写。因为我不能将方法模板与虚拟混合使用。

经过几天的研究,我终于在这里找到了它是如何完成的: C++ is Lazy: CRTP - ModernesCpp.com[^]

CRTP 和静态多态性。最后,我试图实现的行为的 C++ 版本:

// bastAbstractTemplates.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;

template&lt;typename DerivedClass&gt;
class IObjectTransformer {
public:
    template&lt;typename TStructure&gt; TStructure ToStructure(std::vector&lt;unsigned char&gt; bytes);
    template&lt;typename TStructure&gt; std::vector&lt;unsigned char&gt; ToBytes(TStructure structure);
private:
    IObjectTransformer() = default;

    friend DerivedClass;
};

template &lt;typename DerivedClass&gt;
template &lt;typename TStructure&gt;
TStructure IObjectTransformer&lt;DerivedClass&gt;::ToStructure(std::vector&lt;unsigned char&gt; bytes) {
    return static_cast&lt;DerivedClass*&gt;(this)-&gt;ToStructure(bytes);
}

template &lt;typename DerivedClass&gt;
template &lt;typename TStructure&gt;
std::vector&lt;unsigned char&gt; IObjectTransformer&lt;DerivedClass&gt;::ToBytes(TStructure structure) {
    return static_cast&lt;DerivedClass*&gt;(this)-&gt;ToBytes(structure);
}

class ObjectTransformer1 : public IObjectTransformer&lt;ObjectTransformer1&gt; {
public:
    template &lt;typename TStructure&gt; TStructure ToStructure(std::vector&lt;unsigned char&gt; bytes) {
        unsigned char* bytePointer = &amp;bytes[0];
        TStructure structure = reinterpret_cast&lt;TStructure&gt;(*bytePointer);
        return structure;
    }

    template &lt;typename TStructure&gt; std::vector&lt;unsigned char&gt; ToBytes(TStructure structure) {
        char* bytesArray = reinterpret_cast&lt;char*&gt;(&amp;structure);
        auto byteVec = std::vector&lt;unsigned char&gt;(bytesArray, bytesArray + sizeof(TStructure));
        return byteVec;
    }
};

class ObjectTransformer2 : public IObjectTransformer&lt;ObjectTransformer2&gt; {
public:
    template &lt;typename TStructure&gt; TStructure ToStructure(std::vector&lt;unsigned char&gt; bytes) {
        TStructure structure{};
        std::memcpy(&amp;structure, &amp;bytes[0], sizeof(TStructure));
        return structure;
    }
    template &lt;typename TStructure&gt;
    std::vector&lt;unsigned char&gt; ToBytes(TStructure structure) {
        std::vector&lt;unsigned char&gt; bytes{};
        bytes.resize(sizeof(TStructure));
        std::memcpy(&amp;bytes[0], &amp;structure, sizeof(TStructure));
        return bytes;
    }
};


template &lt;typename DerivedClass, typename TStructure&gt;
void CoutStructureBytes(IObjectTransformer&lt;DerivedClass&gt; *objectTransformerBaseAbstractClass, TStructure structure) {
    auto bytes = objectTransformerBaseAbstractClass-&gt;template ToBytes&lt;TStructure&gt;(structure);
    for(auto byte : bytes) {
        std::cout &lt;&lt; std::to_string(byte) &lt;&lt; ' ';
    }
    std::cout &lt;&lt; std::endl;
}

int main() {
    ObjectTransformer1 objectTransformer1{};
    ObjectTransformer1 objectTransformer2{};

    int integer = 5;
    float someFloat = 9.79f;

    CoutStructureBytes(&amp;objectTransformer1, integer);
    CoutStructureBytes(&amp;objectTransformer2, someFloat);
}