c++ - 推导和模板化使用之间有什么区别? (请看上下文)

标签 c++ inheritance c++11 using

我正在编写各种类型的二叉搜索树(经典、splay、reb-black、avl、Treap、随机等)。

对于每种类型的树,我定义了一个通用类,该类接收节点类型、键类型以及键之间的函数比较作为参数。例如,对于 p AVL 树定义(并实现)以下类:

template <template <typename> class NodeType, typename Key, class Compare>
class Gen_Avl_Tree
{
...
};

这种方法的一个原因是将内存的处理与树的处理分开。树不关心分配或释放节点,只是插入、删除或搜索具有键值的节点;等等其他操作。另一个原因是根据应用情况允许节点有或没有虚拟析构函数的可能性。

然后,我定义两个类如下:

    template <typename Key, class Compare = less<Key>>
struct Avl_Tree : public Gen_Avl_Tree<AvlNode, Key, Compare>
{
  using Base = Gen_Avl_Tree<AvlNode, Key, Compare>;
  using Base::Base;
};

    template <typename Key, class Compare = less<Key>>
struct Avl_Tree_Vtl : public Gen_Avl_Tree<AvlNodeVtl, Key, Compare>
{
  using Base = Gen_Avl_Tree<AvlNodeVtl, Key, Compare>;
  using Base::Base;
};

Avl_Tree 使用“普通”节点,Avl_Tree_Vtl 使用带有虚拟析构函数的节点。两种类型都导出嵌套类型Node。例如:Avl_Tree::NodeAvl_Tree_Vtl::Node

认识到这两个类在功能上是相同的,我认为替换以下内容的先前定义更为实际:

    template <typename Key, class Compare = less<Key>>
using struct Avl_Tree = Gen_Avl_Tree<AvlNode, Key, Compare>;

    template <typename Key, class Compare = less<Key>>
using Avl_Tree_Vtl = Gen_Avl_Tree<AvlNodeVtl, Key, Compare>;

但是,当实例化以下函数时,最后一种方法会导致编译器错误(clang 编译器 3.6):

template <template <typename, class> class TreeType,
      typename Key,
      class Compare = Aleph::less<Key>>
tuple<Stat, Stat, int, int> sample_tree(TreeType<Key, Compare> & tree, 
                    gsl_rng * r, int n, int k)
{ ... }

来自另一个函数:

template < template <typename /* key */, class /* Compare */> class TreeType>
void test(unsigned long n, gsl_rng * r)
{
...
   tuple<Stat, Stat, int, int> stats = sample_tree(tree, r, i, log(i)/log(2));
...
}

一行:

test<Avl_Tree>(n, r);

导致错误:

timeAllTree.C:190:6: error: no matching function for call to 'sample_tree'
            sample_tree(tree, r, i, log(i)/log(2)); 
            ^~~~~~~~~~~
timeAllTree.C:344:4: note: in instantiation of function template specialization
      'test<Avl_Tree>' requested here
          test<Avl_Tree>(n, r); 
          ^
timeAllTree.C:56:29: note: candidate template ignored: could not match 'type-parameter-0-1'
      against 'AvlNode'
tuple<Stat, Stat, int, int> sample_tree(TreeType<Key, Compare> & tree, 
                            ^

相比之下,由 Gen_Avl_Tree 派生定义的 Avl_Tree 可以完美编译并运行。

我的问题是,是否有理由相信从 Gen_Avl_Tree 派生的 Avl_Tree 在功能上与使用声明的 Avl_Tree 不同。或者这是编译器的问题

最佳答案

问题是现在类型看起来不同了。最初,你有:

template <typename Key, class Compare = less<Key>>
struct Avl_Tree : public Gen_Avl_Tree<AvlNode, Key, Compare>

因此,Avl_Tree 是 2 种类型的模板:KeyCompare。现在,您拥有:

template <typename Key, class Compare = less<Key>>
using struct Avl_Tree = Gen_Avl_Tree<AvlNode, Key, Compare>;

这里,Avl_Tree 只是 Gen_Avl_Tree 的别名,它是 3 类型的模板:NodeType比较。嗯,NodeType 不是一种类型,它是一个模板,但重点是它是 3 个方面的模板。

现在,你的函数:

template <template <typename, class> class TreeType,
          typename Key,
          class Compare = Aleph::less<Key>>
tuple<Stat, Stat, int, int> sample_tree(TreeType<Key, Compare> & tree, 
                gsl_rng * r, int n, int k)

期望模板采用两种类型。这无法匹配别名 Avl_Tree,因此是完全正确的编译器错误。您可能想做的是:

template <typename Tree>
tuple<Stat, Stat, int, int> sample_tree(Tree& tree, 
                gsl_rng * r, int n, int k)
{
    // or equivalent...
    using Key = typename Tree::Key;
    using Compare = typename Tree::Compare;
    // etc.
}

此版本的函数适用于派生的 Avl_Tree 和别名 Avl_Tree

关于c++ - 推导和模板化使用之间有什么区别? (请看上下文),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27724302/

相关文章:

c++ - 是否可以在不修改父类的情况下将成员变量初始化推迟到继承类?

java - Java 对象的最具体的子类?

C++ - std::array 类型的二维矩阵的定义

java - 使用 Android NDK 获取 ANDROID_ID - 过时的本地引用错误

java - 动态选择类别

C++11:当定义移动构造函数时,按值返回对象不会抛出异常吗?

c++ - 泛型函数中类的泛化为基类创建目标代码?

c++ - unordered_map::insert vs operator []= 函数给出错误

c++ - "has << operator"类型的参数

c++ - CMake: `include_directories` 与 `target_link_libraries` 有什么区别