Этот код работает:

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 рекомендует использовать прозрачный функтор и изменить std::less<uint64_t> на std::less<> в моем объявлении _reverse. К сожалению, когда я это делаю, код больше не компилируется:

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 дает мне аналогичную ошибку.

Почему типизированный функтор в порядке, но прозрачный функтор не компилируется?

0
Thomas Johnson 23 Сен 2018 в 01:30

2 ответа

Лучший ответ

Компаратор по умолчанию для 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.

2
Miles Budnek 22 Сен 2018 в 22:51

Проблема в том, что функция сравнения является частью типа экземпляра шаблона 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;
};
1
Michael Kenzel 22 Сен 2018 в 23:01