为什么我不能 return 具有透明仿函数的法线贴图引用?

Why can't I return a normal map reference with a transparent functor?

此代码有效:

class MyObj {};

class MyData {
 public:
  using tMyMap = std::map<uint64_t, std::shared_ptr<MyObj>>;

  const tMyMap& get_normal() const;
  const tMyMap& get_reverse() const;

 private:
  std::map<uint64_t, std::shared_ptr<MyObj>> _normal;

  std::map<uint64_t, std::shared_ptr<MyObj>, std::less<uint64_t>> _reverse;
};

const MyData::tMyMap& get_normal() const { return _normal; }
const MyData::tMyMap& get_reverse() const { return _reverse; }

但 clang-tidy 建议我使用透明仿函数,并在我的 _reverse 声明中将 std::less<uint64_t> 更改为 std::less<>。不幸的是,当我这样做时,代码不再编译:

test_map.cpp: In member function ‘const tMyMap& MyData::get_reverse() const’:
test_map.cpp:20:60: error: invalid initialization of reference of type ‘const tMyMap& {aka const std::map<long unsigned int, std::shared_ptr<MyObj> >&}’ from expression of type ‘const std::map<long unsigned int, std::shared_ptr<MyObj>, std::less<void> >’
 const MyData::tMyMap& MyData::get_reverse() const { return _reverse; }

该错误消息来自 g++,但 clang 给了我一个类似的错误。

为什么有类型的仿函数没问题,而透明的仿函数却编译不了?

问题在于比较函数是 std::map 模板实例类型的一部分。 std::map<K, T> 使用的比较函数的默认值为 std::less<T>。所以

std::map<uint64_t, std::shared_ptr<MyObj>, std::less<uint64_t>>

std::map<uint64_t, std::shared_ptr<MyObj>>

碰巧是同一类型(第二个版本将使用默认参数作为比较函数,结果是std::less<uint64_t>)。但是,如果你用std::less<>(相当于std::less<void>)作为你对_reverse的比较函数,那么_normal_reverse的类型就不会转出来一样了。只有 _normal 仍然是 tMyMap 类型,它使用默认比较函数,而 _reverse 将是不同的、不相关的类型。

如果 MyData 的目的只是为了保存这两个映射,您可能需要考虑将其转换为结构:

struct MyData {
  std::map<uint64_t, std::shared_ptr<MyObj>> normal;
  std::map<uint64_t, std::shared_ptr<MyObj>, std::less<>> reverse;
};

或者只使用auto以避免重复每个地图的类型

class MyData {
public:
  const auto& get_normal() const { return _normal; }
  const auto& get_reverse() const { return _reverse; }

private:
  std::map<uint64_t, std::shared_ptr<MyObj>> _normal;
  std::map<uint64_t, std::shared_ptr<MyObj>, std::less<>> _reverse;
};

std::map 的默认比较器是 std::less<KeyType>.

这意味着

std::map<uint64_t, T>

std::map<uint64_t, T, std::less<uint64_t>>

是同一类型,但是

std::map<uint64_t, T, std::less<>>

是不同的类型,因为std::less<uint64_t>std::less<>是不同的类型。


如果 _normal_reverse 实际上彼此相反,您会看到同样的问题。我假设您实际上是想将 _reverse 声明为

std::map<uint64_t, std::shared_ptr<MyObj>, std::greater<uint64_t>> _reverse;

所以它的顺序与_normal相反。