如何设置预期的模板参数基本类型

huangapple go评论70阅读模式
英文:

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 &lt;class T&gt;
  /**
   * @brief Loading dictionary from database
   * @param sqlQuery sql query to execute
   * @return Dictionary of type T
   * !!! T MUST BE DERIVED FROM BaseDicModel !!!
   */
  static QList&lt;T *&gt;
  getDictionary(QString sqlQuery) {
    QList&lt;T *&gt; result;
    QString conName = &quot;regionConnection&quot;;
    {
      QSqlDatabase conn = getConnectionV2(
          conName, ConfigService::getInstance()-&gt;get_db_connection());
      //        int ret = -1;
      bool bOpen = false;
      if (!conn.isOpen()) {
        if (!conn.open()) {
          qWarning() &lt;&lt; &quot;Error while open db&quot;;
          return result;
        }
        bOpen = true;
      }

      QSqlQuery *query = new QSqlQuery(conn);

      query-&gt;prepare(sqlQuery);
      if (query-&gt;exec()) {
        while (query-&gt;next()) {
          T *row = new T();
          row-&gt;parseQuery(query);
          result.append(row);
        }
      } else {
        qWarning() &lt;&lt; &quot;Error while exec query:&quot; &lt;&lt; query-&gt;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&lt;class T implements BaseDicModel&gt;

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_ofenable_if将是主要工具,可以选择添加is_same作为可选项。

所以,从上到下,首先应该添加额外的参数,我选择了一个void指针。非类型参数的逻辑在于,如果尝试手动指定该参数,检查将始终适用:

  1. typename std::enable_if&lt;?????&gt;::type* = nullptr
  2. 内部条件检查T是否是某些东西的基类,因此std::is_base_of&lt;Base, T&gt;::value
  3. 可选择地,可以添加第二个检查来验证T是否不是基本类型本身 &amp;&amp; !std::is_same&lt;T, Base&gt;::value
  4. 所以将它们全部放在一起:template &lt;typename T, typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value &amp;&amp; !std::is_same&lt;T, Base&gt;::value&gt;::type* = nullptr&gt;
#include &lt;type_traits&gt;

struct Base
{
    virtual ~Base() = default;
};

struct DerivedPublic : Base{};

class DerivedPrivate : Base{};

struct DerivedFurther : DerivedPrivate{};

// 派生 + 基类
template &lt;typename T, typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value&gt;::type* = nullptr&gt;
// 仅派生类
// template &lt;typename T, typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value &amp;&amp; !std::is_same&lt;T, Base&gt;::value&gt;::type* = nullptr&gt;
struct S{};

int main(int, char*[])
{
    S&lt;Base&gt; sb;
    S&lt;DerivedPublic&gt; sdpub;
    S&lt;DerivedPrivate&gt; sdprv;
    S&lt;DerivedFurther&gt; sdf;
    // S&lt;int&gt; 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:

  1. typename std::enable_if&lt;?????&gt;::type* = nullptr
  2. The condition inside checks if T is base of something, therefore std::is_base_of&lt;Base, T&gt;::value
  3. Optionally, a second check could be added to verify whether T is not the base type itself &amp;&amp; !std::is_same&lt;T, Base&gt;::value
  4. So putting it all together: template &lt;typename T, typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value &amp;&amp; !std::is_same&lt;T, Base&gt;::value&gt;::type* = nullptr&gt;
#include &lt;type_traits&gt;


struct Base
{
    virtual ~Base() = default;
};

struct DerivedPublic : Base{};

class DerivedPrivate : Base{};

struct DerivedFurther : DerivedPrivate{};

// derived + base
template &lt;typename T, typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value&gt;::type* = nullptr&gt;
// derived only
// template &lt;typename T, typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value &amp;&amp; !std::is_same&lt;T, Base&gt;::value&gt;::type* = nullptr&gt;
struct S{};

int main(int, char*[])
{
    S&lt;Base&gt; sb;
    S&lt;DerivedPublic&gt; sdpub;
    S&lt;DerivedPrivate&gt; sdprv;
    S&lt;DerivedFurther&gt; sdf;
    // S&lt;int&gt; si;
}


Demo

huangapple
  • 本文由 发表于 2023年6月4日 23:47:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76401218.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定