通过容器和元素类型模板将参数传递给函数

时间:2019-08-29 02:15:28

标签: c++ templates

这应该是一个简单的解决方法,但是我困惑地在网上寻找如何传递具有容器和元素类型模板的函数的参数。我具有二进制搜索树的以下实现,并且在主函数中调用它会引发参数与函数模板不匹配的错误。

#include <iostream>
#include <vector>

template <typename ElementType>
struct BTreeNode
{
    ElementType value;
    BTreeNode<ElementType>* left;
    BTreeNode<ElementType>* right;
};

template <typename ElementType>
void Insert(BTreeNode<ElementType> ** root, int value)
{
    if (*root == NULL)
    {
        *root = new node(value);
    }
    else if ((*root)->value <= value)
    {
        insert(&((*root)->pRight), value);
    }
    else if ((*root)->value > value)
    {
        insert(&((*root)->pLeft), value);
    }
}


template <typename ContainerType, typename ElementType> 
BTreeNode<ElementType>* CreateBST(const ContainerType & elements, const size_t num_elements)
{
    BTreeNode<ElementType> * root = NULL;
    for (int i = 0; i < num_elements; ++i)
    {
        Insert(&root, elements[i]);
    }
    return root;
}


int main()
{
    std::vector<int> x = { 10, 5, 15, 5, 6, 7, 8, 89 };
    BTreeNode<int> * tree = CreateBST(x, x.size());
    //inOrderTraversal(pRoot);
    std::cout << std::endl;
    return 0;
}

3 个答案:

答案 0 :(得分:2)

问题是ElementType的第二个模板参数CreateBST不可推导,您需要像

那样明确指定它
BTreeNode<int> * tree = CreateBST<std::vector<int>, int>(x, x.size());
//                               ^^^^^^^^^^^^^^^^^^^^^^^

或者您可以删除模板参数ElementType,使CreateBSTContainerType获取(例如STL容器的成员类型为value_type)。

template <typename ContainerType> 
BTreeNode<typename ContainerType::value_type>* CreateBST(const ContainerType & elements, const size_t num_elements)
{
    BTreeNode<typename ContainerType::value_type> * root = NULL;
    for (int i = 0; i < num_elements; ++i)
    {
        Insert(&root, elements[i]);
    }
    return root;
}

然后您可以像使用它

BTreeNode<int> * tree = CreateBST(x, x.size());

答案 1 :(得分:1)

#include <iostream>
#include <vector>
using namespace std;

template <typename ElementType>
struct BTreeNode
{
    ElementType value;
    BTreeNode<ElementType>* left;
    BTreeNode<ElementType>* right;
    BTreeNode(ElementType & t):value(t){}
};

template <typename ElementType>
void Insert(BTreeNode<ElementType> ** root, ElementType value)
{
    if (*root == NULL)
    {
        *root = new BTreeNode<ElementType>(value);
    }
    else if ((*root)->value <= value)
    {
        Insert(&((*root)->right), value);
    }
    else if ((*root)->value > value)
    {
        Insert(&((*root)->left), value);
    }
}


template <typename ContainerType, typename ElementType>
BTreeNode<ElementType>* CreateBST(const ContainerType & elements, const size_t 
num_elements)
{
     BTreeNode<ElementType> * root = NULL;
     for (int i = 0; i < num_elements; ++i)
     {
         Insert<ElementType>(&root, elements[i]);
     }
     return root;
 }


 int main()
 {
    std::vector<int> x = { 10, 5, 15, 5, 6, 7, 8, 89 };
    BTreeNode<int> * tree = CreateBST<vector<int>, int>(x, x.size());
    //inOrderTraversal(pRoot);
    std::cout << std::endl;
    return 0;
 }

您的代码中几乎没有其他错误,包括不初始化动态/模板参数类型。为了使编译器在编译时导出模板化参数的类型,必须在创建对象或进行方法调用时传递模板类型(将要使用的参数的实际数据类型)。由于C / C ++是类型化语言,因此它们需要在编译时解析变量/参数的类型。对于模板类,您不必使用method_name<typename>格式来显式地进行方法调用,只需使用类型足以初始化编译器的类实例来初始化其实例中定义的所有方法的类型。但是对于类外的方法,每次调用方法时都需要指定类型。

答案 2 :(得分:1)

编译器无法计算出您对ElementType的调用类型为CreateBST(),因为该函数的任何参数都没有约束它。

您有两种选择:

明确指定类型

auto* tree = CreateBST<decltype(x), int>(x, x.size());

又矮又甜,也许有点尴尬。

从ContainerType派生ElementType

大多数标准库容器公开了value_type typedef,它们是它们存储的类型:

template <typename ContainerType, typename ElementType = typename ContainerType::value_type> 
BTreeNode<ElementType>* CreateBST(const ContainerType & elements, const size_t num_elements)
{
    BTreeNode<ElementType> * root = NULL;
    for (int i = 0; i < num_elements; ++i)
    {
        Insert(&root, elements[i]);
    }
    return root;
}

这将起作用,并且相当简单,但是它不适用于您可能希望作为ContainerType传递的所有内容-您的函数支持带有operator[]的任何内容,并且仅适用于暴露value_type typedef。

从ContainerType :: operator []的返回值中派生ElementType。

template <typename ContainerType, typename ElementType = std::remove_reference_t<decltype(std::declval<ContainerType>()[0])>> 
BTreeNode<ElementType>* CreateBST(const ContainerType & elements, const size_t num_elements)
{
    BTreeNode<ElementType> * root = NULL;
    for (int i = 0; i < num_elements; ++i)
    {
        Insert(&root, elements[i]);
    }
    return root;
}

其中有几个活动部分:

  • 我们要获取ContainerType::operator[]的返回类型,因此我们通过std::declval<ContainerType>()实例化ContainerType的实例,然后调用operator[]
  • 我们使用decltype()
  • 获得该表达式的类型
  • 大多数operator []函数返回一个引用,但我们不希望这样做,因此我们将结果传递给std::remove_reference
相关问题