-3

I created a class Route which I wanted to store in an std::set. A Route is indexed by an Id, so what I want is to be able to have an expression like

class RouteTemplate
{
    Route *RouteTemplate::getRoute(const char *pId);
    Route::ptr_set mRoutes;
};

Route *RouteTemplate::getRoute(const char *pId)
{
    Route::ptr_set::const_iterator pos = mRoutes.find(pId);
    if(pos == mRoutes.end())
        return NULL;

    return *pos;
}

However I get a compiler error.

conversion from 'const char *' to 'Route *const ' not possible

As far as I know I have to implement the comparator, which I did.

class Route
{
public:
    static const size_t _id_len = 11;

    class comparator
    {
    public:
        bool operator() (const Route &oLeft, const Route &oRight) const
        {
            return oLeft < oRight;
        }
    };
    class ptr_comparator
    {
    public:
        bool operator() (const Route *oLeft, const Route *oRight) const
        {
            return (*oLeft) < (*oRight);
        }
    };

    typedef std::set<Route, Route::comparator> set;
    typedef std::set<Route *, Route::ptr_comparator> ptr_set;

public:
    Route(void);
    Route(const char *oId);
    virtual ~Route(void) {};

    inline bool operator<(const Route &oOther) const
    {
        return strncmp(mId, oOther.mId, _id_len) < 0;
    }

    inline bool operator<(const char *oId) const
    {
        if(!oId)
            return false;

        return strncmp(mId, oId, _id_len) < 0;
    }

    inline const char *getId(void) const { return mId; }
    inline void setId(const char *oId)
    {
        if(oId == NULL)
            mId[0] = 0;
        else
        {
            strncpy(mId, oId, sizeof(mId));
            mId[_id_len] = 0;
        }
    }

private:
    char mId[_id_len+1];
    // Additional members
};
Devolus
  • 21,661
  • 13
  • 66
  • 113
  • 1
    [`std::find_if`](http://en.cppreference.com/w/cpp/algorithm/find) – Biffen Nov 02 '15 at 15:42
  • 1
    …but what's the question?! – Biffen Nov 02 '15 at 15:43
  • @Biffen: `find_if` will be `O(n)` whereas it can theoretically be done in `O(log n)` – Jarod42 Nov 02 '15 at 15:45
  • @Jarod42 OK. Is that what this is about? – Biffen Nov 02 '15 at 15:46
  • @Jarod42 How do you imagine it can be done in log(n)? If you sort set by one comparator and search items by another? –  Nov 02 '15 at 15:50
  • @Satus You can't do that using `std::set::find`. The sort/search criteria must be the same. – juanchopanza Nov 02 '15 at 15:57
  • @juanchopanza, but since I always reference the id, why would the search criteria be not the same? I also added the appropriate constructor, so I don't understand why this doesn't work. – Devolus Nov 02 '15 at 15:57
  • @Devolus I didn't say it wasn't. I was responding to a comment from somebody else. But you should probably simplify your example into an MCVE. – juanchopanza Nov 02 '15 at 15:58
  • @juanchopanza I know that =) That's why I was asking Jarod42. –  Nov 02 '15 at 16:00
  • @Satus OK. I got confused because your question has no relevance to Jarod's comment or the code posted here. – juanchopanza Nov 02 '15 at 16:03
  • @Satus: The search use the "same" criteria, but with the *extracted* value of the key. OP may create a dummy object with the `id` value and search the dummy object, but it is not always possible. – Jarod42 Nov 02 '15 at 16:24

2 Answers2

2

I assume you want to leverage the templated overload of std::set::find that was added in C++14. Before that, you could only find() a key of the Key type that is used for the std::set. So, the first thing to do is using a C++14 compiler.

Second, that additional overload can only function if the resulting comparison has the same semantics as would have constructing a (temporary) key and comparing it with the std::sets comparator. If I'm not missing anything, your comparators would qualify for this. However, to avoid accidental mistakes, you have to explicitly confirm that by giving the Compare type a type member is_transparent.

If you can live with a temporary being created, you could explicitly ask for it. This should work.

Route *RouteTemplate::getRoute(const char *pId)
{
    Route temporary_key {pId};
    Route::ptr_set::const_iterator pos = mRoutes.find(&temporary_key);
    if(pos == mRoutes.end())
        return NULL;
    return *pos;
}
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • I don't have a C++14 compiler. Currently I use Visual Studio 2010 which is about C++11 (not fully). So if I understand your comment, this will not work because that funcitionallity isd missing in the compiler? But even so, shouldn't the constructor make sure that the comparison still works? It worked for the non-pointer version at least. – Devolus Nov 02 '15 at 16:01
  • You can construct a temporary `Route` from a `const char[]` (you wrote a constructor for this conversion). But you cannot construct a temporary `Route *` from a `const char *`. As far as Visual Studio is concerned, I don't know about their support but the feature was proposed by one of its maintainers so you might have a chance that it was adopted early. You'll have to consult your documentation. The official introduction was in C++14. – 5gon12eder Nov 02 '15 at 16:05
  • I tried that as well, but then I get different compiler errors. I try to create an SSCE. – Devolus Nov 02 '15 at 16:11
0

You could also overload operator&, to allow invoking it on temporary object. This would simplify usage o find method as you could create Route object on the fly and then apply operator& at this temporary.

class Route
{
public:
...
    Route* operator&() { return this; }
...
}

Then it would be valid to write getRoute() method like:

Route *RouteTemplate::getRoute(const char *pId)
{
    Route::ptr_set::const_iterator pos = mRoutes.find(&Route(pId));
    if (pos == mRoutes.end())
        return NULL;

    return *pos;
}
dbajgoric
  • 1,447
  • 11
  • 17