【问题标题】:c++ functions as template argumentsc++ 函数作为模板参数
【发布时间】:2023-03-03 16:20:01
【问题描述】:

我遇到了一些问题,可以通过以下代码恢复:

template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return fct(p.first);
}

int main(int argc, char *argv[])
{
  size_t val = 
    wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));

  return 0;
}

我正在使用 clang 编译器 3.4 版,此代码无法编译并出现以下错误

test-tmp.C:17:5: error: no matching function for call to 'wrapper'
    wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test-tmp.C:9:8: note: candidate template ignored: invalid explicitly-specified argument
      for template parameter 'fct'

这个想法是在std::pair 上包装一个哈希函数(模板参数fct),只取第一个字段。

dft_hash_fct是另一个模板,定义如下:

template <typename Key>
size_t dft_hash_fct(const Key & key)
{
  return SuperFastHash(key);
}

这个通用函数有效;它已在其他情况下使用。

所有这些的目的是重用基于哈希的集合(而不是映射)作为任何类型项目的键映射。基于散列的 ser 在构造时接收一个散列函数。

感谢您的 cmets(David、Andrey 和 Kazark)

已编辑:

嗯,我明白了,typename fct 是一个类型,所以我不能作为指针函数处理;对不起琐事。不幸的是,我认为在包装器中将函数作为参数传递的方法不起作用,因为哈希集需要一个具有以下签名的函数指针:

size_t (*the_function)(const Key & key);

因此,意识到这一点,感谢您的观察,我将相关代码更改为:

template <typename Key, typename Data, size_t (*fct)(const Key & k)>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return (*fct)(p.first);
}

int main(int argc, char *argv[])
{
  size_t val = 
    wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));

  return 0;
}

编译、链接和运行。另外,我把这行:

size_t (*fct)(const std::pair<int, int>&) = 
    wrapper<int, int, dft_hash_fct<int>>;

cout <<  (*fct)(std::pair<int, int>(4,6)) << endl;

编译,链接 ans 也运行。所以,我可以说编译器(当然根据语言)可以实例化函数并处理指向它的函数指针。

因此,在那之后我尝试修改我的原始代码,它是 HashSet 的派生类,用于管理由第一个字段散列的对。

我声明一些为:

template <typename Key, typename Data>
class HashMap : public HashSet<std::pair<Key, Data>>
{
  ...
  HashMap(size_t (*function)(const Key & key))
    : HashSet<Key, Data>(wrapper<Key, Data, function>)
  {

  }

..
};

但是编译(使用 std=c++11)失败并出现错误

./tpl_dynSetHash.H:353:7: error: no matching constructor for initialization of
      'HashSet<std::pair<unsigned long, long>>'
    : HashSet<std::pair<Key,Data>(
      ^
testDynSetHash.C:178:8: note: in instantiation of member function
      'HashMap<unsigned long, long>::HashMap' requested here
  HMap table; 

但是,如果我用

代替对基本构造函数的调用
: HashSet<Key, Data>(wrapper<Key, Data, dft_hash_fct<Key>)

编译得很好。因此,我认为问题出在参数类型声明上(但我不知道是什么)。

【问题讨论】:

  • 你需要为类型模板参数提供一个类型,但是你提供了一个函数。
  • 您使用的是 clang++3.4,您可以/是否允许使用 C++11 功能?
  • 1) 您在最后一个代码块中的wrapper&lt;Key, Data, dft_hash_fct&lt;Key&gt; 末尾缺少&gt;。 2)HashMap(size_t (*function)(const Key &amp; key)) : HashSet&lt;Key, Data&gt;(wrapper&lt;Key, Data, function&gt;) 是不可能的,因为function 在这里不是编译时常量(而是函数参数);只允许编译时常量(常量表达式)作为非类型模板参数(我指的是wrapper&lt;..&gt; 的第三个参数)。
  • 1) 是的,这是一个错字;编译。 2) 我明白了,谢谢。任何解决方法?我想要一个 HashMap 构造函数,它接收函数指针(与 HashSet 相同)并传递给 HashSet 构造函数;以包装形式,因为键是第一个字段,而不是整个对
  • 不允许将运行时值用作模板非类型参数。只有编译时值可以用作模板参数。这就是为什么它使用显式 dft_hash_fct&lt;int&gt; 作为模板参数(编译时已知的值),但如果函数指针来自构造函数参数(编译时未知的值)则不起作用。如果您想使用运行时函数指针,您别无选择,只能使用我在回答中描述的方法。 IE。函数指针必须通过函数参数到达,而不是通过模板参数。

标签: c++ templates clang


【解决方案1】:

传递函数的标准习惯用法是将它们作为函数对象传递,例如

template <typename Key, typename Data, typename Fct>
size_t wrapper(const std::pair<Key, Data> & p, Fct fct)
{
  return fct(p.first);
}

然后使用以下方法调用包装器:

int main(int argc, char *argv[])
{
  // no explicit template arguments required
  size_t val = 
    wrapper(std::pair<int,int>(5,9), &dft_hash_fct<int>);

  return 0;
}

另一方面,在您的代码中:

template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return fct(p.first);
}

typename fct 为类型引入了别名。在这个函数中,fct 命名了一个类型;因此fct(p.first) 创建了一个fct 类型的对象,该对象需要转换为size_t 才能从wrapper 返回。您也可以使用它,但您必须使用的类型必须如下所示:

struct dft_hash_fct_t
{
    size_t result;
    dft_hash_fct_t(int p) : result(SuperFashHash(p)) {}
    operator size_t() const { return result; }
};

这可能不是你想要的。

【讨论】:

    【解决方案2】:

    中的模板声明

    template <typename Key, typename Data, typename fct>
    size_t wrapper(const std::pair<Key, Data> & p)
    {
      return fct(p.first);
    }
    

    将模板参数fct 声明为一种类型,但您正试图将函数指针传递给它。您可以像这样使fct函数指针模板参数:

    template <typename Key, typename Data, size_t(*fct)(const Key&)>
    size_t wrapper(const std::pair<Key, Data> & p)
    {
      return fct(p.first);
    }
    

    然而,更惯用的方法是(正如 DyP 所说)传递一个函数对象,以便该函数与函数指针以及重载的对象一起工作 operator()

    template <typename Key, typename Data, typename Fct>
    size_t wrapper(const std::pair<Key, Data> & p, Fct fct)
    {
      return fct(p.first);
    }
    

    然后在调用它时将函数作为参数传递

    wrapper(std::pair<int,int>(5,9), dft_hash_fct<int>);
    

    【讨论】:

      【解决方案3】:

      您编写的代码在您的意图上下文中毫无意义。您的fct 模板参数是一个类型。这意味着

      return fct(p.first);
      

      是一个函数风格的转换,而不是() 运算符的应用(即它不是函数调用)。在您的代码中,您尝试将p.first 转换为类型fct,然后尝试将该转换的结果返回为size_t。那是你的意图吗?我怀疑是这样的。最重要的是,您正在尝试将函数指针 value dft_hash_fct&lt;int&gt; 作为 fct 的模板参数传递,即您正在传递一个 value 其中一个 类型 是预期的。您期望它如何工作?

      您提供的描述似乎暗示您实际上想从wrapper 内部调用类型为fct 的函子,而不是执行强制转换。为了做到这一点,你必须以某种方式获得函子本身。再次记住fct 不是函子,它只是函子的类型

      典型的方法是从外部传递函子,作为函数参数

      template <typename Key, typename Data, typename fct>
      size_t wrapper(const std::pair<Key, Data> & p, fct f)
      {
        return f(p.first);
      }
      

      现在您可以将 wrapper 模板与基于类的函子以及普通函数一起使用

      size_t val = wrapper(std::pair<int,int>(5,9), dft_hash_fct<int>);
      

      请注意,dft_hash_fct&lt;int&gt; 必须作为函数参数而不是模板参数提供。 无需显式指定模板参数,因为它们将由编译器推导出来。

      【讨论】: