the_mandrill
the_mandrill

Reputation: 30842

How to wrap a templated function to work on const and non-const data

I want to create a templated function that works the same way for const and non-const data, except that it returns a const or non-const pointer as appropriate.

For instance, I want to return a pointer to a matching element in a container:

template <class Container, class Pred>
typename Container::value_type* getValuePtrIf(Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}

However I can't get this to build for const and non-const calls. If I omit the const from the Container& c declaration then it can't return a const pointer, but if I change to const Container& c then I can return a const pointer, but then the non-const case doesn't build.

Is there a way of defining this so that the const is interpreted as being part of the Container template parameter so that I only have to define one version of this function?

Upvotes: 5

Views: 223

Answers (2)

Robert Ramey
Robert Ramey

Reputation: 1164

The simplest is to define both templates and let the compiler find the best match

template <class Container, class Pred>
typename const Container::value_type* getValuePtrIf(const Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}
template <class Container, class Pred>
typename Container::value_type* getValuePtrIf(Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}

If you object to duplicating code (a worth objection) due to maintenance issues you can try something like the following:

template <class Container, class Pred>
typename const Container::value_type* getValuePtrIf(const Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}
template <class Container, class Pred>
typename Container::value_type* getValuePtrIf(Container& c, Pred pred)
{
    return const_cast<Container::value_type*>(
        getValuePtrIf(const_cast<const Container &>( c ), pred)
    );
}

If it were me, I'd also replace Pred with const Pred & pred)

Robert Ramey

Upvotes: -1

Arne Mertz
Arne Mertz

Reputation: 24606

From the code it seems you have C++11 support in your compiler. Then you can probably use decltype and trailing return types as well:

template <class Container, class Pred>
auto getValuePtrIf(Container& c, Pred pred) -> decltype(&*std::begin(c))
{
    auto it=std::find_if(std::begin(c), std::end(c), pred);
    return (it!=std::end(c)) ? &(*it) : nullptr;
}

it will be of whatever type std::begin(c) gives you (iterator or const_iterator), so the type of &(*it) is the same as of decltype(&*std::begin(c)).

Upvotes: 6

Related Questions