vakker
vakker

Reputation: 45

How to return map with different comparator in C++?

If I have a class with 2 std::map properties, both with different comparators, then how can I write a function to return either of them? E.g.:

class Test {
public:
  std::map<int, int> &get_map(int id) {
    if (id == 1)
      return map_1;
    else
      return map_2;
  }

private:
  std::map<int, int, std::less<int>> map_1;
  std::map<int, int, std::greater<int>> map_2;
};

Here the compiler will fail because map_2 is not std::map<int, int>. I also tried std::map<int, int, std::binary_function<int, int, bool>> as the return type, but that doesn't work either.

Upvotes: 2

Views: 361

Answers (4)

ks1322
ks1322

Reputation: 35706

Another option is to write your own comparator with parameter:

struct Cmp {
    Cmp(bool is_less) : is_less(is_less)
    {}

    bool operator()(int lhs, int rhs) const { 
        if (is_less) {
            return lhs < rhs;
        } else {
            return lhs > rhs;
        }
    }

private:
    bool is_less;
};

Now you can use it in both of std::map with different value of is_less parameter:

class Test {
public:
  Test() :
    map_1(Cmp(true)),
    map_2(Cmp(false))
  {}

  std::map<int, int, Cmp> &get_map(int id) {
    if (id == 1)
      return map_1;
    else
      return map_2;
  }

private:
  std::map<int, int, Cmp> map_1;
  std::map<int, int, Cmp> map_2;
};

Upvotes: 1

Jarod42
Jarod42

Reputation: 217145

As alternative, you might erase comparer type, to have same map type:

class Test {
public:
  std::map<int, int, bool (*)(int, int)> &get_map(int id) {
      return (id == 1) ? map_1 : map_2;
  }

private:
  std::map<int, int, bool (*)(int, int)> map_1 = {+[](int lhs, int rhs) { return lhs < rhs; }};
  std::map<int, int, bool (*)(int, int)> map_2 = {+[](int lhs, int rhs) { return lhs > rhs; }};
};

Upvotes: 2

eerorika
eerorika

Reputation: 238311

There is no way to write a function that can return either directly, because they have a different type (with no common base class). You can however have a function template:

template<class Comp>
std::map<int, int, Comp>& get_map();

Here, id is redundant because the member is identified by the template argument.


Or you could wrap a pointer to either in a tagged union, and return that. For example:

std::variant<
    std::map<int, int, std::less<int>>*,
    std::map<int, int, std::greater<int>>*
>
get_map(int id);

Object oriented approach could be to not provide a getter at all, and only operate on the maps within the class.

Upvotes: 4

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122228

Short answer: You cannot.

Templates are just templates. Different instantiations of templates are different types. If map would be your type, then you could make different instantiations inherit from a common base and use them polymorphically. However, std::map is not your type, so you cannot do that. You could return a std::variant, but writing two getters is much easier.

Last but not least, note that if you return a non-const reference to a private member, then there is practiaclly no encapsulation. You could as well make the members public, which will also eliminate your current problem.

Upvotes: 2

Related Questions