1

When writing a template function like:

template<class T> void print(T const & collection)

When looping through the collection and dereferencing the iterator everything works right if you have something like vector<int> unless you change it to vector<int*>. What's the best way to deal with the differences in a single template function whilst not duplicating code?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Blair Davidson
  • 901
  • 12
  • 35
  • 1
    So you need to support both value types and reference types? What are you doing inside the `print()` method? If you're only passing them around, you're good with a single implementation. If you need to access members, then you'll need SFINAE to switch at compile time between implementations for value or reference types. – Bret Kuhns Jan 13 '13 at 02:34
  • Provide a template function for pointer types that simply dereferences and passes to the reference type function? That seems simple enough, am I missing something? – Chad Jan 13 '13 at 02:37
  • You'll need access to the underlying `value_type` of the collection (thankfully, most expose it), whatever it is. Then as Bret described, an SFINAE set of contrived specializations is probably a good path. Honestly you'd be better off probably writing a `print()` that takes iterators and specializes off the `iterator_traits<>` class info. – WhozCraig Jan 13 '13 at 02:45
  • How would SFINAE help if value_type is a shared_ptr I still need to dereference it example – Blair Davidson Jan 13 '13 at 02:48
  • @BlairDavidson I don't have a compiler at hand, but if something like `std::is_pointer` doesn't work on `unique_ptr`/`shared_ptr`, then you could add specializations for those two smart pointers. Considering Microsoft's STL implementation adds these specializations for a lot of their stuff, I'd have to assume that's the pragmatic solution... – Bret Kuhns Jan 13 '13 at 03:00
  • 1
    It's not clear what a print(vector) call would actually be expected to print. Would you want it to print the values of the pointers in the vector, or the values of the integers the pointers point to? – Jeremy Friesner Jan 13 '13 at 03:53
  • Write a function that pretty prints an element. Well, a set of overridden functions. A template on `T*`, another on shared ptrs, etc. You could probably use SFINAE to make both `T*` and shared ptr be the same implementation, but I won't recommend it. – Yakk - Adam Nevraumont Jan 13 '13 at 09:01

1 Answers1

0

I would write a single template function do_print that delegates to a class template printer. The class template is a function object that does the pretty printing, and that you partially specialize for T* by simply calling the pretty print version on *t.

So there is no duplication of the pretty printing code and a minor inconvenience for writing two lightweight implementation classes (these get optimized away by any modern compiler, so there is no runtime overhead).

I prefer this solution over SFINAE tricks because partial class specialization gives you much more control (and much better error messages) than function overloading tricks. It's also recommended by the Alexandrescu & Sutter Coding Standards.

BTW, this code will also work for T** because the specialization for T* delegates to the code for T. So T** is send to T* and finally to T. In fact, arbitrary levels of indirection get reduced to printing the elements pointed to by pointers.

#include <iostream>
#include <vector>

namespace detail {

template<typename T>
struct printer
{
   void operator()(T const& t) 
   { 
      std::cout << t; // your pretty print code here
   }  
};

template<typename T>
struct printer<T*>
{
   void operator()(T const* t) 
   { 
      printer<T>()(*t); // delegate to printing elements (no duplication of prettty print)
   }
};

}

template<typename T>
void do_print(T const& t)
{
   detail::printer<T>()(t);
}

template<typename C>
void print(C const& collection)
{
   for(auto&& c: collection) 
      do_print(c);
   std::cout << "\n";
}

int main()
{
   int a = 1;
   int b = 2;

   auto c = &a;
   auto d = &b;

   std::vector<int> v1 { a, b };
   std::vector<int*> v2 { c, d };
   std::vector<int**> v3 { &c, &d };

   print(v1);
   print(v2);
   print(v3);
}

Output on Live Work Space

TemplateRex
  • 69,038
  • 19
  • 164
  • 304