模板上的隐秘错误

时间:2016-03-24 00:34:42

标签: c++ templates c++11

我对我目前正在处理的一段代码所看到的错误消息感到困惑。我试图提取代码中最相关的部分,以便在眼睛上轻松实现。

我看到的错误:

  

错误:没有匹配函数来调用“Map(Print&, std::shared_ptr< LinkedList< int> >&)”   注意:模板参数扣除/替换失败:   无法将“p”(类型“Print”)转换为“int”类型

为什么编译器会尝试将Print强制转换为int

template<typename T>
class LinkedList
{
public:
  using NodeType = std::shared_ptr<LinkedList>;
  ... Other things
}

template<class Func, typename T>
typename LinkedList<T>::NodeType Map(Func func, typename  LinkedList<T>::NodeType seq)
{
  // Some stuff
}

class Print
{
 public:

 int operator()(int i)
 {
     cout << i << endl;
     return i;
 }
};

void main()
{
  Print p;
  auto one = std::make_shared< LinkedList <int>> ();
  auto result = Map<int>(p, one);  << ---- Error
}

gcc 4.8.4

感谢阅读。

2 个答案:

答案 0 :(得分:1)

如果我这样做,我会将Map定义为LinkedList的内联朋友。这消除了依赖类型推导的问题,因此您可以在没有任何显式模板参数的情况下调用Map

请记住,编译器找到这样定义的函数的唯一方法是使用Argument-Dependent Lookup - 因此它仅在LinkedList对象作为函数参数之一传递时才有效。内联朋友似乎也吓坏了人们。

但是,清理和简化代码还有很长的路要走。

#include <iostream>
#include <memory>

template<typename T>
class LinkedList
{
public:
    using NodeType = std::shared_ptr<LinkedList>;


    template <typename Func>
    friend NodeType Map(Func func, NodeType seq) {
        return seq;
    }

};

class Print
{
public:
    int operator()(int i)
    {
        std::cout << i << std::endl;
        return i;
    }
};

int main()
{
  Print p;
  auto one = std::make_shared< LinkedList<int> >();
  auto result = Map(p, one);
}

答案 1 :(得分:0)

在更好地了解OP的目标后,我正在添加一个新的答案。我的第一个答案解决了在编译一些示例代码时遇到的与依赖模板类型参数相关的困难,所以我认为它仍然是一个有效的答案。

这个答案演示了如何创建自定义容器类以及如何编写泛型Map函数,该函数迭代容器,为每个元素调用自定义函数对象,并将每个结果添加到最终由功能

此示例中的容器是一个链接列表,使用一组最小的功能定义,仅适用于此示例的编译和运行。

链接列表提供begin()end()函数,这些函数返回用于遍历列表的迭代器对象。这里只定义了非常量版本,当LinkedList对象为const时,还应该添加返回常量迭代器的版本。

这里的Map函数将functor和容器对象作为参数。推导出所有参数类型,因此无需显式提供模板参数。容器通过值传递以避免LinkedList中的const-correctness问题,并且因为LinkedList的(默认)复制构造函数没有做很多工作 - 它只需要复制两个{{1对象,而不是执行所有包含数据的深层副本。

这个shared_ptr非常简单,其他改进版本可能需要两个迭代器(开始和结束),对容器对象进行const引用(以避免无用的复制),或使用类型特征来处理容器需要以特殊方式访问。

就像它在这里一样,Map应该与其他容器一起工作,比如std :: vector,因为它使用标准的迭代器API。

LinkedList的原始版本仅作为指向单个列表节点的指针进行维护。在下面的代码中,Map是一个真正的容器对象,它保存(智能)指向第一个和最后一个LinkedList个对象的指针。我们需要在这里访问指向Nodehead节点的指针,因此保持指向一个节点的指针是不合适的。

tail