【问题标题】:Smart pointers as map key智能指针作为地图键
【发布时间】:2014-12-02 20:46:08
【问题描述】:

我有以下代码来测试智能指针作为std::map 的键,我在 Mac 和 Linux 上运行代码,但我观察到不同的输出,这是一个错误还是我做错了什么?

#include <iostream>
#include <memory>
#include <string>
#include <map>

using namespace std;

class Dog {
 public:
   typedef shared_ptr<Dog> sptr;

   Dog(const string &name) : name_(name) { }

   friend bool operator<(const Dog &lhs, const Dog &rhs) {
     cout << "Dog::operator< called" << endl;
     return lhs.name_ < rhs.name_;
   } 

   friend bool operator<(const sptr &lhs, const sptr &rhs) {
     cout << "Dog::operator< sptr called" << endl;
     return lhs->name_ < rhs->name_;
   } 

 private:
   string name_;
};

void test_raw_object_as_map_key() {
  cout << "raw object as map key ============== " << endl;
  map<Dog, int> m;
  m[Dog("A")] = 1;
  m[Dog("B")] = 2;
  m[Dog("C")] = 3;
  m[Dog("A")] = 4;

  cout << "map size: " << m.size() << endl;
}

void test_smart_pointer_as_map_key() {
  cout << "smart pointer as map key ============== " << endl;

  map<Dog::sptr, int> m;
  m[make_shared<Dog>("A")] = 1;
  m[make_shared<Dog>("B")] = 2;
  m[make_shared<Dog>("C")] = 3;
  m[make_shared<Dog>("A")] = 4;

  cout << "map size: " << m.size() << endl;
}

int main(int argc, const char *argv[]) {
  test_raw_object_as_map_key();
  test_smart_pointer_as_map_key();
  return 0;
}

在 Mac 上:

neevek@MAC$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix

neevek@MAC$ ./a.out
raw object as map key ============== 
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
map size: 3
smart pointer as map key ============== 
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
Dog::operator< sptr called
map size: 3

在 Linux 上:

neevek@LINUX$ g++ --version
g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

neevek@LINUX$ ./a.out
raw object as map key ============== 
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
Dog::operator< called
map size: 3
smart pointer as map key ============== 
map size: 4

【问题讨论】:

  • 顺便说一句,第二个operator &lt;不需要是朋友,可以是return *lhs &lt; *rhs;
  • @o11c,是的,但更改没有任何区别。
  • 关键是std::shared_ptr&lt;T&gt; 中的T 中的任何T 都有一个operator&lt;,而您现在已经为std::shared_ptr&lt;Dog&gt; 提供了一个重载,它只能是由 ADL 发现。我不知道哪个编译器选择了正确的,或者是否涉及 UB。

标签: c++ c++11 map shared-ptr smart-pointers


【解决方案1】:

GCC 是正确的(在 Mac 上,您看到 g++ 实际上是 clang):std::map 使用 std::less&lt;T&gt; 来比较密钥。这反过来在参数上调用operator &lt;,但查找首先在namespace std 中执行,因此它找到shared_ptr 的默认实现,比较内部指针。要完成这项工作,您必须将std::less 专门用于shared_ptr&lt;Dog&gt;

namespace std {
    template<>
    struct less<shared_ptr<Dog>> {
        bool operator() (const shared_ptr<Dog>& lhs, const shared_ptr<Dog>& rhs) {
            return *lhs < *rhs;
        }
    };
}

【讨论】:

  • +1。有趣的事实是,如果它在命名空间 std 之外,它会反过来工作 - OP 的 operator &lt; 不是模板,而 std::operator &lt; 是。
【解决方案2】:

std::map 的默认 Compare 对象是 std::less&lt;Key&gt;,在您的情况下是 std::shared_ptr&lt;Dog&gt;。所以它寻找std::less&lt; std::shared_ptr&lt;Dog&gt; &gt;的实现,它只是比较指针地址。

要指定Compare对象,你可以自己定义并在map中使用

class MyCompare {
public:
  bool operator() (const sptr& l, const sptr& r) {
    return *l < *r; // Invokes your Dog's operator <
  }
};

然后

  map<sptr, int, MyCompare> m;
  m[make_shared<Dog>("A")] = 1;
  m[make_shared<Dog>("B")] = 2;
  m[make_shared<Dog>("C")] = 3;
  m[make_shared<Dog>("A")] = 4;

  cout << "map size: " << m.size() << endl;

输出map size: 3

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 2011-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多