c++ - 调用派生类型的模板化函数

标签 c++ c++11 templates c++14 decltype

我正在尝试对值调用模板化函数,但我想调用来自潜在派生类型的函数的模板化版本。在 typeid 期间确定类型为 Dog,但任何转换尝试都会失败。如何调用派生类型模板化 WriteData 函数?

关于示例

  1. WriteData 函数是模板化的,尽管本示例中未使用该值(因为我做了一个小示例)。
  2. 并不总是知道 Array 参数将是 Animal
#include <iostream>
using namespace std;

struct Writer {
    void Write(char* data) {
        cout << data << endl;
    }
};

template<typename Type>
struct Array {
    Type Data[2];

    template<typename Formatter>
    void WriteData(Formatter formatter) {
        cout << "Data[0](typeid): " << typeid(*Data[0]).name() << endl;
        cout << "Data[1](typeid): " << typeid(*Data[1]).name() << endl;

        Data[0]->WriteData(formatter); // "Animal is doing something"
        Data[1]->WriteData(formatter); // "Dog is doing something"

        auto data1cast = reinterpret_cast<decltype(Data[1])>(Data[1]);
        cout << "data1cast(typeidformatter " << typeid(*data1cast).name() << endl; // data1cast(typeid): class Dog
        (*data1cast).WriteData(formatter); // "Animal is doing something"

        ((Dog*)Data[1])->WriteData(formatter); // "Dog is doing something"
    }
};

struct Animal {
    virtual ~Animal() = default;

    template<typename Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write("Animal is doing something");
    }
};

struct Dog : Animal {
    template<typename Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write("Dog is doing something");
    }
};

int main(void) {
    Array<Animal*> arr;
    arr.Data[0] = new Animal();
    arr.Data[1] = new Dog();

    Writer writer;
    arr.WriteData(writer);

    system("PAUSE");

    return 0;
}

输出

Data[0](typeid): struct Animal
Data[1](typeid): struct Dog
Animal is doing something
Animal is doing something
data1cast(typeid): struct Dog
Animal is doing something
Dog is doing something

最佳答案

模板和虚拟 cannot be mixed :

template<typename Formatter>
virtual void WriteData(Formatter formatter);

将是无效声明。

A member function template cannot be virtual, and a member function template in a derived class cannot override a virtual member function from the base class.

这里有多种选择,具体取决于您想要实现的灵活性以及您面临的其他限制。例如:

1.

你可以引入一个抽象类Formatter:

struct Formatter {
    virtual ~Formatter() = default;
    virtual void Write(const std::string&) = 0;
};

struct Writer : Formatter {
    void Write(const std::string& data) override {
        std::cout << data << std::endl;
    }
};

并使 WriteData 成为非模板:

struct Animal {
    virtual ~Animal() = default;

    virtual void WriteData(Formatter& formatter) {
        formatter.Write("Animal is doing something");
    }
};

struct Dog : Animal {
    void WriteData(Formatter& formatter) override {
        formatter.Write("Dog is doing something");
    }
};

2.

使 WriteData 成为一个非虚拟模板,调用虚拟成员函数来获取应传递给 Formatter 的信息:

struct Writer {
    template<class T>
    void Write(const T& data) {
        std::cout << data << std::endl;
    }
};

struct Animal {
    virtual ~Animal() = default;

    template<class Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write(MyName() + " is doing something");
    }

    virtual std::string MyName() {
        return "Animal";
    }
};

struct Dog : Animal{
    virtual std::string MyName() {
        return "Dog";
    }
};

3.

根本不要使用虚函数,在编译时调度 WriteData 调用。使用 std::tuple 代替数组来存储不同的类型:

struct Writer {
    template<class T>
    void Write(const T& data) {
        std::cout << data << std::endl;
    }
};

template<typename... Types>
struct Array {
    std::tuple<Types...> Data;

    Array(Types... data) : Data(std::move(data)...) {}

    template<typename Formatter>
    void WriteData(Formatter formatter) {
        std::get<0>(Data).WriteData(formatter);
        std::get<1>(Data).WriteData(formatter);
    }
};

struct Animal {
    template<typename Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write("Animal is doing something");
    }
};

struct Dog { // can be derived from Animal, but here it doesn't matter
    template<typename Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write("Dog is doing something");
    }
};

int main() {
    Array<Animal, Dog> arr{Animal(), Dog()};   // <...> can be omitted in C++17

    Writer writer;
    arr.WriteData(writer);
}

4.

您可以在 if-else 分支内使用 dynamic_cast:

struct Writer {
    template<class T>
    void Write(const T& data) {
        std::cout << data << std::endl;
    }
};

struct Animal {
    virtual ~Animal() = default;

    template<typename Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write("Animal is doing something");
    }
};

struct Dog : Animal {
    template<typename Formatter>
    void WriteData(Formatter formatter) {
        formatter.Write("Dog is doing something");
    }
};

template<typename Type>
struct Array {
    Type Data[2];

    template<typename Formatter>
    void WriteData(Formatter formatter) {
        for (auto d : Data) {
            if (auto dog = dynamic_cast<Dog*>(d))
                dog->WriteData(formatter);
            else
                d->WriteData(formatter);
        }
    }
};

这个设计很糟糕。每次修改动物的层次结构时,都必须更新Array::WriteData

关于c++ - 调用派生类型的模板化函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59128937/

相关文章:

c++ - “error: incompatible types in assignment of” 尝试调整对象指针数组的大小时

面向 Ruby 脚本编写者的 C++

c++ - 是否可以在标准 C++ 中打印变量的类型?

c++ - 如何将 std::chrono::time_point 转换为 uint64_t?

c++ - 标准库方法的成员函数指针问题

c++ - 本着 boost spirit ,将 multi_pass 与流文件输入一起使用,这是迭代器需要的

c++ - Windows MS VC++ 2005 上的 CRT 堆调试

C++ 模板 : Automatically overload templated function with const& for constant params?

c++ - 隐藏成员函数模板 - 哪个编译器是正确的?

C++模板实例化: Avoiding long switches