Let's say I have a template function that takes a range of constants (or better, start and end-iterators) of some sort of pointer collection. This function internally creates an STL container with pointers for reorganizing items.
Now I want to use this function again for unique_ptr collections. I somehow need to change the template parameters or introduce a new shell or overload ... but how? Is there any magic to the C ++ 11 template, STL helper or helper helper? Following the code example:
#include <string> #include <iostream> #include <vector> #include <algorithm> #include <memory> // Element Class class Foo { }; // Take a range of elements, sort them internally by their addresses and print them in order template <typename FooIterator> void print_sorted_addresses(FooIterator beginFoos, FooIterator endFoos) { // Sort them std::vector<const Foo*> elements(beginFoos, endFoos); std::sort(elements.begin(), elements.end()); // Print them for(const auto& e : elements) std::cout << e << std::endl; } int main() { std::vector<Foo*> raw_foos; std::vector<std::unique_ptr<Foo>> unique_foos; // Fill them for(int i=0; i<10; i++) { std::unique_ptr<Foo> foo(new Foo()); raw_foos.push_back(foo.get()); unique_foos.push_back(std::move(foo)); } print_sorted_addresses(raw_foos.cbegin(), raw_foos.cend()); //print_sorted_Foos(unique_foos.cbegin(), unique_foos.cend()); // ERROR return 0; }
The offender, apparently, is the uneven behavior of the source pointers and smart pointers (in particular, unique_ptr ) to convert them as source pointers. This can either be bypassed through the à la std::addressof(*p) dereferencing cycle, but it only has well-defined behavior if p is not nullptr . To reduce any runtime checks, I played with conditional patterns and came up with the following:
template<typename Ptr> using RawPtr = typename std::pointer_traits<Ptr>::element_type*; // raw pointers like int**, const char*, ... template<typename Ptr> typename std::enable_if<std::is_pointer<Ptr>::value, RawPtr<Ptr>>::type make_raw(Ptr ptr) { return ptr; } // smart pointers like unique_ptr, shared_ptr, ... template<typename Ptr> typename std::enable_if<!std::is_pointer<Ptr>::value, RawPtr<Ptr>>::type make_raw(Ptr& ptr) { return ptr.get(); }
This can be used in the @tclamb iterator or in boost :: transform_iterator, as in @Praetorian's answer. But it still seems strange to use a specific get () - a member of the smart-pointer implementation instead of the * -interface operator, which makes the pointer a pointer.