我在一个类中声明了一个模板成员函数,它根据类型调用正确的成员函数,我想通过添加一个成员函数在子类中为它添加一些功能,如下面的main.cpp示例所示:
#include <iostream>
class A
{
public:
template <typename T>
void handleSocketData(const T& t)
{
handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data) const
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<int>(30);
b.handleSocketData<std::string>("Hi");
return 0;
}
我的问题是b.handleSocketData<QString>("Hi");
实际上在A类中生成了一个新的模板示例,如/usr/bin/clang++ -DQT_CORE_LIB -isystem /usr/include/qt6/QtCore -isystem /usr/include/qt6 -isystem /usr/lib64/qt6/mkspecs/linux-g++ -g -std=gnu++17 -Xclang -ast-print -fsyntax-only main.cpp
命令的输出所示:
class A {
public:
template <typename T> void handleSocketData(const T &t) {
this->handleData(t);
}
template<> void handleSocketData<int>(const int &t) {
this->handleData(t);
}
template<> void handleSocketData<std::basic_string<char>>(const std::basic_string<char> &t) {
<recovery-expr>(this->handleData, t);
}
void handleData(int data) {
std::cout << data << std::endl;
}
};
class B : public A {
public:
void handleData(std::string data) const {
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[]) {
A a;
B b;
a.handleSocketData<int>(30);
b.handleSocketData<std::string>("Hi");
return 0;
}
所以现在我有一个编译错误,说没有找到函数handleData(const std::string& data),这是正常的。
我们发现的一个变通方法是定义一个双参数模板,将子类作为参数(一种访问者模式):
#include <iostream>
class A
{
public:
template <typename T, typename U>
void handleSocketData(U& u, const T& t)
{
u.handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data)
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<int>(a, 30);
b.handleSocketData<std::string>(b, "Hi");
return 0;
}
你觉得怎么样?有没有更干净的方法?
2条答案
按热度按时间yzuktlbb1#
这看起来像是CRTP的经典用例。您可以将
A
作为派生类Derived
上的模板,然后通过static_cast
将函数调用分派给派生类。要实现此功能,任何派生类Derived
都必须派生自A<Derived>
。由于您似乎希望将
A
用作非抽象类,因此必须添加一个默认派生类,将其标记为"final"。在以下代码中,空结构FinalTag
用于此目的。验证码:https://godbolt.org/z/ns9aPjG76
这是一个原型,例如,你可能想给
cast
方法添加一个const版本。编辑:
正如Jarod42在评论中指出的,C ++23通过"推导这个"确实简化了CRTP:https://godbolt.org/z/cGzMrnEhc。不过,目前编译器并不广泛支持此功能。
vjrehmav2#
与Joerg Brech建议的版本稍有不同的CRTP版本在某些情况下可能更合适。
它与您的解决方案非常相似,我们指示
handleSocketData
应该使用哪个类来调用handleData
,唯一的区别是不是动态地而是在编译时做出决定。