Martin Drozdik
Martin Drozdik

Reputation: 13313

When writing generic code for smart pointers and pointers, how to get the simple pointer?

When I want to implement a function:

template <typename TIterator>
void doSomething(TIterator begin, TIterator end);

to be able to work with both:

std::vector<Widget*>

and

std::vector<std::shared_ptr<Widget>>

how should I access the underlying pointer? For some reason std::shared_ptr<Widget> is not implicitly convertible to Widget* and I have to use the get() function. This way, the code does not work for std::vector<Widget*> since Widget* has no get() function.

What I do is this:

void functionAcceptingPointer(Widget* widget);

template <typename TIterator>
void doSomething(TIterator begin, TIterator end);
{
    std::map<double, decltype(&(**begin))> map;
    ...
    auto firstPointer = &(**begin);
    ...
    functionAcceptingPointer(&(**begin));
    ....
}

That is use the double dereference * followed by the & operator, whenever I need an ordinary pointer.

It does the job, but I am curious if there is some more readable, generally used way to do this and if there are some things that can come back to haunt me after I fill my code with ugly stuff like &(**begin)

Upvotes: 3

Views: 337

Answers (2)

Howard Hinnant
Howard Hinnant

Reputation: 219185

Here's what I do to solve this problem:

#include <memory>

template <class T>
inline
T*
to_raw_pointer(T* p) noexcept
{
    return p;
}

template <class Pointer>
inline
typename std::pointer_traits<typename std::remove_reference<Pointer>::type>::element_type*
to_raw_pointer(Pointer&& p) noexcept
{
    return ::to_raw_pointer(p.operator->());
}
int main()
{
    std::shared_ptr<int> p1(new int(1));
    int* r1 = to_raw_pointer(p1);
    int* p2 = new int(2);
    int* r2 = to_raw_pointer(p2);
}

Upvotes: 11

Daniel Frey
Daniel Frey

Reputation: 56883

To increase the readability, you can create your own wrapper to dereference any (smart) pointer:

template<typename T> auto raw_deref(T t) -> decltype( &**t ) { return &**t; }

which would turn your code into

template <typename TIterator>
void doSomething(TIterator begin, TIterator end)
{
    std::map<double, decltype(raw_deref(begin))> map;
    ...
    auto firstPointer = raw_deref(begin);
    ...
    functionAcceptingPointer(raw_deref(begin));
    ....
}

and of course you could think of a better name than raw_deref :)

Upvotes: 3

Related Questions