0

我有点坚持使用带有指针委托的集合。我的代码如下:

void Graph::addNodes (NodeSet& nodes)
{ 
  for (NodeSet::iterator pos = nodes.begin(); pos != nodes.end(); ++pos)
  { 
    addNode(*pos);
  }
}

这里 NodeSet 定义为:

typedef std::set<Node_ptr, Node_ptr_Sorting_Predicate> NodeSet;

上面的代码在我的 Windows 机器上运行良好,但是当我在 MAC 上运行相同的代码时,它给了我以下错误:

Graph::addNode(const boost::shared_ptr<Node>&)调用 ' '没有匹配的函数

仅供参考, Node_ptr 的类型为:typedef boost::shared_ptr<Node> Node_ptr;

有人可以告诉我为什么会这样吗?

4

2 回答 2

1

好的,根据您添加的信息,问题似乎是每个非引用addNode都需要一个,而编译器必须调用该函数的是一个(注意)。让我解释:Node_ptrconstconst boost::shared_ptr<Node>&const

std::set是一个关联容器。关联容器以某种顺序存储它们的元素,使用关键元素来定义排序。如果允许您在容器不知情的情况下更改密钥,您将使容器的内部顺序无效。这就是为什么我认为取消引用 astd::set<T>::iterator不会返回可修改的左值。(这意味着您不能更改返回的引用。例如,如果您有一个指向 a 的迭代器posstd::set<int>*pos=42不应编译。)
这样做的问题是只有可修改的左值才会绑定到非const引用。但是*pos返回的不是可修改的左值,因此不会。(因此,在我的示例中,int& r = *pos;不会编译。)原因是,如果允许,您可以通过非const在容器背后引用并弄乱容器的内部顺序。
这就是为什么你的结果*pos不会绑定到Node_ptr&. 这反过来就是编译器无法调用您的函数的原因。

你的addNode()成员函数真的改变了它给出的节点吗?如果没有,它应该采取const Node_ptr&.
如果是这样,则说明您有设计问题。您不能更改集合中的元素。您唯一能做的就是将其从集合中移除、更改并重新添加。

附带说明:VC9 确实编译了以下代码:

#include <iostream>
#include <set>
#include <typeinfo>
#include <iterator>

int main()
{
    std::set<int> set;
    set.insert(5);
    std::cout << *set.begin() << '\n';
    *set.begin() = 3; // this is an error!
    std::cout << *set.begin() << '\n';
    return (0);
}

我相信这是VC9中的一个错误。科莫拒绝了。


以下是如何通过编译器不调用您认为它应该调用的函数或从一组重载中调用错误的函数来解决谜题。
你认为它应该调用的函数是Graph::addNode(Node_ptr&). 您认为应该调用它的代码是

addNode(*pos);

更改该代码,使其提供所需的确切参数:

Node_ptr& tmp = *pos;
addNode(tmp);

现在调用肯定会编译(或调用正确的重载),如果编译器认为*pos不能分配给Node_ptr&.
通常这种策略可以帮助我找出在这种情况下出了什么问题。

于 2010-05-17T21:46:05.277 回答
0

如果有记忆,原始 C++ 规范 (1998) 允许 std::set 返回可修改的迭代器。这带来了风险——迭代器可能被用来修改存储的值,这样集合的顺序现在就被破坏了。我相信规范的后续版本已经改变了这一点,现在所有的集合迭代器都是不可修改的。

VC++ 2010 尊重新行为,并且具有不可修改的集合迭代器(这很烦人,因为它阻止了不改变顺序且应该合法的更改)。

然而,以前的版本没有。这意味着您可以创建未使用 const 适当注释的函数,这将导致切换到不同编译器时出现问题。解决方案是添加必要的 const 更改。VC++ 仍然可以工作(因为非 const 值无论如何都可以隐式地设为 const),其他一切也可以。

于 2010-05-18T09:01:49.993 回答