我正在尝试对值调用模板化函数,但我想调用来自潜在派生类型的函数的模板化版本。在 typeid
期间确定类型为 Dog
,但任何转换尝试都会失败。如何调用派生类型模板化 WriteData
函数?
关于示例
WriteData
函数是模板化的,尽管本示例中未使用该值(因为我做了一个小示例)。- 并不总是知道
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/