0

I've got a bit of a conundrum here. I am writing a mesh (grid) generator. In doing so, I need to obtain a list of lower dimensional elements from higher dimensional elements (i.e. a "triangle" element is composed of 3 "line" elements). I need the list of unique line elements and I need the parent element to store a pointer to each of its unique child elements.

To this end, I create a std::set of smart pointers to the child elements and try to insert each child element to the list. Every time I try to insert an element I ask for a pair containing an iterator to the pre-existing element and a boolean specifying whether or not the element has been inserted.

The problem is that std::set (and map) return constant iterators to the pre-existing element. However, I need to give the parent element of the element that I failed to insert a non-constant pointer to the pre-existing element. So I use const_pointer_cast<> to cast the constant iterator to a non-constant one.

Is there a better way of going about this? Doing a const_pointer_cast() is probably not ideal. Is there something that I should be using for this application instead of std::set?

Edit: here's the function:

template<class T1, class T2>
int getUniqueSubelements(std::set<std::shared_ptr<T1>, elGreater>& elements,
                         std::set<std::shared_ptr<T2>, elGreater>& childels)
{
    typedef std::shared_ptr<T2> child_ptr;

    std::pair<typename std::set<child_ptr, elGreater>::iterator, bool> ret;

    for (auto parit = elements.begin(); parit != elements.end(); ++parit)
    {
        (*parit)->generateChildren();

        const std::vector<child_ptr>& children = (*parit)->getChildren();

        for (int i = 0; i < children.size(); i++)
        {
            ret = childels.insert(children[i]);

            if (ret.second == false)
            {
                child_ptr temp = std::const_pointer_cast<T2>(*ret.first);
                bool orient = elOrientation(children[i], temp);
                children[i]->swapInParent(temp, orient);
            }
        }
    }
    return 1;
}
  • `"The problem is that std::set (and map) return constant iterators to the pre-existing element."` - Can you show us code to prove that please? – David G May 17 '14 at 23:18
  • Your assertion is flat out [wrong](http://en.cppreference.com/w/cpp/container/set/insert). You get a (non-const) iterator back from insertions. The problem is that set iterators don't allow you to mutate set elements. (Map iterators, by contrast, are very different.) – Kerrek SB May 17 '14 at 23:22
  • The whole description is hard to follow and gives me the feeling that you should code your own class. There's a reason why the keys of `std::set` and `std::map` are constants. http://stackoverflow.com/questions/10498292/c-why-is-const-cast-evil – 101010 May 17 '14 at 23:23
  • Could you add some sample code demonstrating your situation? – Kerrek SB May 17 '14 at 23:23
  • There is not a single `shared_ptr` anywhere in this code. There are some `const shared_ptr&` maybe, but those can be copied just fine. – aschepler May 18 '14 at 02:23

1 Answers1

0

No cast at all is needed here.

It's true that std::set<K, Comp>::iterator is the same as std::set<K, Comp>::const_iterator, and that iterator::operator* returns a const K&.

But in your case, this means that the type of *ret.first is const std::shared_ptr<T2>&. This is the analogue of (T2* const), not (const T2*): you can't modify the smart pointer, but you can modify the actual stored object.

So assuming elOrientation is something like

template <typename T>
bool elOrientation(std::shared_ptr<T>, std::shared_ptr<T>);

you can just call elOrientation(children[i], *ret.first).

aschepler
  • 70,891
  • 9
  • 107
  • 161