英文:
How to set expected template argument base type
问题
我有一个接口基类:
class BaseDicModel {
public:
virtual ~BaseDicModel() {}
virtual QString text() const = 0;
virtual int id() const = 0;
virtual void parseQuery(QSqlQuery *q) = 0;
virtual QJsonObject toJson() = 0;
};
以及一个模板函数,该函数应根据模板类执行 SQL 查询并返回相应的字典:
template <class T>
/**
* @brief 从数据库加载字典
* @param sqlQuery 要执行的 SQL 查询
* @return T 类型的字典
* !!! T 必须派生自 BaseDicModel !!!
*/
static QList<T *>
getDictionary(QString sqlQuery) {
QList<T *> result;
QString conName = "regionConnection";
{
QSqlDatabase conn = getConnectionV2(
conName, ConfigService::getInstance()->get_db_connection());
// int ret = -1;
bool bOpen = false;
if (!conn.isOpen()) {
if (!conn.open()) {
qWarning() << "打开数据库时出错";
return result;
}
bOpen = true;
}
QSqlQuery *query = new QSqlQuery(conn);
query->prepare(sqlQuery);
if (query->exec()) {
while (query->next()) {
T *row = new T();
row->parseQuery(query);
result.append(row);
}
} else {
qWarning() << "执行查询时出错:" << query->lastError().text();
}
delete query;
if (bOpen) { conn.close(); }
}
QSqlDatabase::removeDatabase(conName);
return result;
}
};
它运行正常,但我有一个问题:模板类现在可以是任何类,而我想要的是仅限于派生自 BaseDicModel 的模板类。类似于这样的伪代码:
template<class T implements BaseDicModel>
你能否给我一些提示,如何使其正常工作?
问题可能有点傻,但我似乎无法理解。谢谢。
英文:
I have an interface base class:
class BaseDicModel {
public:
virtual ~BaseDicModel() {}
virtual QString text() const = 0;
virtual int id() const = 0;
virtual void parseQuery(QSqlQuery *q) = 0;
virtual QJsonObject toJson() = 0;
};
and a template function, which is supposed to execute sql query and return dictionary depending on template class.
template <class T>
/**
* @brief Loading dictionary from database
* @param sqlQuery sql query to execute
* @return Dictionary of type T
* !!! T MUST BE DERIVED FROM BaseDicModel !!!
*/
static QList<T *>
getDictionary(QString sqlQuery) {
QList<T *> result;
QString conName = "regionConnection";
{
QSqlDatabase conn = getConnectionV2(
conName, ConfigService::getInstance()->get_db_connection());
// int ret = -1;
bool bOpen = false;
if (!conn.isOpen()) {
if (!conn.open()) {
qWarning() << "Error while open db";
return result;
}
bOpen = true;
}
QSqlQuery *query = new QSqlQuery(conn);
query->prepare(sqlQuery);
if (query->exec()) {
while (query->next()) {
T *row = new T();
row->parseQuery(query);
result.append(row);
}
} else {
qWarning() << "Error while exec query:" << query->lastError().text();
}
delete query;
if (bOpen) { conn.close(); }
}
QSqlDatabase::removeDatabase(conName);
return result;
}
};
it works fine, but i have a problem: template class can be ANY class right now,and what i'd like to get is template only for BaseDicModel derived classes.
Something like this pseudocode:
template<class T implements BaseDicModel>
Can you please give me any hints on how to make it work?
Question must be silly, but i don't seem to get it.
Thanks.
答案1
得分: 1
我个人会仅仅依赖鸭子类型,不关心,除了在您的上下文中将T重命名为有意义的东西,例如DicModel。
在C++20之前,这变得有点混乱,但可以通过添加第二个模板参数来完成。我更喜欢非类型参数,因为它对抵御检查的超越提供了稍微更好的保护,但类型参数也应该可以做到。
我猜is_base_of
和enable_if
将是主要工具,可以选择添加is_same
作为可选项。
所以,从上到下,首先应该添加额外的参数,我选择了一个void
指针。非类型参数的逻辑在于,如果尝试手动指定该参数,检查将始终适用:
typename std::enable_if<?????>::type* = nullptr
- 内部条件检查T是否是某些东西的基类,因此
std::is_base_of<Base, T>::value
- 可选择地,可以添加第二个检查来验证T是否不是基本类型本身
&& !std::is_same<T, Base>::value
- 所以将它们全部放在一起:
template <typename T, typename std::enable_if<std::is_base_of<Base, T>::value && !std::is_same<T, Base>::value>::type* = nullptr>
#include <type_traits>
struct Base
{
virtual ~Base() = default;
};
struct DerivedPublic : Base{};
class DerivedPrivate : Base{};
struct DerivedFurther : DerivedPrivate{};
// 派生 + 基类
template <typename T, typename std::enable_if<std::is_base_of<Base, T>::value>::type* = nullptr>
// 仅派生类
// template <typename T, typename std::enable_if<std::is_base_of<Base, T>::value && !std::is_same<T, Base>::value>::type* = nullptr>
struct S{};
int main(int, char*[])
{
S<Base> sb;
S<DerivedPublic> sdpub;
S<DerivedPrivate> sdprv;
S<DerivedFurther> sdf;
// S<int> si;
}
英文:
Personally I would just rely on duck typing and don't care, maybe apart from renaming T to something meaningful in you context, e.g. DicModel.
It gets a bit ugly pre-C++20, but it can be done by adding a second template argument. I prefer a non-type one, as it offers slightly better protection against outsmarting the check, but a type one should also do.
I guess is_base_of
and enable_if
are going to be the main workhorses, with the optional addition of is_same
.
So, going top down, first the extra parameter should be added, I have chosen a pointer to void. The logic behind a non-type parameter is that if one tries to specify that argument manually, the check applies regardless of it:
typename std::enable_if<?????>::type* = nullptr
- The condition inside checks if T is base of something, therefore
std::is_base_of<Base, T>::value
- Optionally, a second check could be added to verify whether T is not the base type itself
&& !std::is_same<T, Base>::value
- So putting it all together:
template <typename T, typename std::enable_if<std::is_base_of<Base, T>::value && !std::is_same<T, Base>::value>::type* = nullptr>
#include <type_traits>
struct Base
{
virtual ~Base() = default;
};
struct DerivedPublic : Base{};
class DerivedPrivate : Base{};
struct DerivedFurther : DerivedPrivate{};
// derived + base
template <typename T, typename std::enable_if<std::is_base_of<Base, T>::value>::type* = nullptr>
// derived only
// template <typename T, typename std::enable_if<std::is_base_of<Base, T>::value && !std::is_same<T, Base>::value>::type* = nullptr>
struct S{};
int main(int, char*[])
{
S<Base> sb;
S<DerivedPublic> sdpub;
S<DerivedPrivate> sdprv;
S<DerivedFurther> sdf;
// S<int> si;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论