Почему я не могу вернуть ссылку на карту нормалей с прозрачным функтором? - PullRequest
0 голосов
/ 23 сентября 2018

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

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 выдает мне похожую ошибку.

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

Ответы [ 2 ]

0 голосов
/ 23 сентября 2018

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.

0 голосов
/ 23 сентября 2018

Проблема в том, что функция сравнения является частью типа экземпляра шаблона 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;
};
...