模板上的密码错误

Cryptic error on templates

本文关键字:密码 错误      更新时间:2023-10-16

我看到的一段代码的错误消息让我很困惑。我试着提取最相关的代码片段,让它看起来很容易。

我看到的错误:

错误:对"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

感谢阅读。

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

请记住,编译器查找这样定义的函数的唯一方法是使用参数相关查找,因此只有当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);
}

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

这个答案演示了创建一个自定义容器类,以及如何编写一个在容器上迭代的通用Map函数,为每个元素调用一个自定义函数对象,并将每个结果添加到另一个容器中,该容器最终由函数返回。

这个例子中的容器是一个链表,用一组最小的特性定义,只够这个例子编译和运行。

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

这里的Map函数采用一个函子和一个容器对象作为参数。所有参数类型都是推导出来的,因此不需要显式提供模板参数。容器是按值传递的,以避免LinkedList中的常量正确性问题,也因为LinkedList的(默认)复制构造函数不需要做很多工作——它只需要复制两个shared_ptr对象,而不需要对所有包含的数据执行深度复制。

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

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

LinkedList的原始版本只是作为指向单个列表节点的指针进行维护。在下面的代码中,LinkedList实际上是一个容器对象,它保留了指向第一个和最后一个Node对象的(智能)指针。我们需要同时访问指向headtail节点的指针,因此只保留指向一个节点的指针是不够的。

#include <iostream>
#include <memory>
#include <string>
template<typename T>
class LinkedList {
public:
    struct Node;
    using NodeType = std::shared_ptr<Node>;
    struct Node {
        NodeType   next;
        T          value;
    };
    class forward_iterator {
        NodeType cur_node;
    public:
        forward_iterator() {}
        forward_iterator(NodeType cur_node) : cur_node(cur_node) {}
        T& operator * () { return cur_node->value; }
        forward_iterator& operator ++ () { cur_node = cur_node->next; return *this; }
        bool operator == (const forward_iterator& it) const { return cur_node == it.cur_node; }
        bool operator != (const forward_iterator& it) const { return !operator == (it); }
    };
    void push_back(const T& t) {
        NodeType node = std::make_shared<Node>();
        if(tail) {
            tail->next = node;
            tail = node;
        } else {
            head = tail = node;
        }
        node->value = t;
    }
    forward_iterator begin() { return forward_iterator(head); }
    forward_iterator end()   { return forward_iterator(); }
protected:
    NodeType head, tail;
};
inline std::string upper_case(const std::string& s) {
    std::string r;
    for(auto c : s) {
        if(c >= 'a' && c <= 'z') c = c - 'a' + 'A';
        r.push_back(c);
    }
    return r;
}
template <typename Func, typename S>
inline S Map(Func func, S seq) {
    S result;
    for(const auto& elem : seq) {
        result.push_back(func(elem));
    }
    return result;
}

int main() {
    // add strings to a LinkedList of strings named "original"
    static const char* my_data[] = { "Hello", "1234", "John Cena", "xd" };
    LinkedList<std::string> original;
    for(auto cstr : my_data) { original.push_back(cstr); }
    // dump "original" to cout
    std::cout << "-- original --n";
    for(const auto& s : original) { std::cout << s << 'n'; }
    std::cout << 'n';
    // Run the generic Map function with "original" as input
    // A lambda will be called for each element and
    // its returned value added to the result, named "mapped".
    // A functor object may be used in place of the lambda
    auto mapped = Map(
        [](const std::string& s) { return upper_case(s); },
        original);
    // dump "mapped" to cout
    std::cout << "-- mapped --n";
    for(const auto& s : mapped) { std::cout << s << 'n'; }
    std::cout << 'n';
}