{"id":1151,"date":"2013-11-16T01:35:55","date_gmt":"2013-11-15T23:35:55","guid":{"rendered":"http:\/\/blog.ulrichard.ch\/?p=1151"},"modified":"2013-11-16T01:35:55","modified_gmt":"2013-11-15T23:35:55","slug":"enable_if-revisited","status":"publish","type":"post","link":"https:\/\/ulrichard.ch\/blog\/?p=1151","title":{"rendered":"revisiting enable_if"},"content":{"rendered":"<p>It was roughly 2008, when I wanted to make a template function for serialization, only available to container types. Template stuff can become complicated at times, and from reading the documentation <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_55_0\/libs\/utility\/enable_if.html\" target=\"_blank\" rel=\"noopener\">boost::enable_if<\/a> seemed to be just what I needed. I didn&#8217;t get it to work, and I blamed Microsoft Visual Studio 2005 for not being standards compatible enough. And somehow I remembered <strong>enable_if<\/strong> as being difficult and hard to get to work, despite highly desirable if it would work. I ended up providing explicit template overloads for all the supported container types.<\/p>\n<p>Fast forward to five years later, enable_if made it into the C++11 standard, and I didn&#8217;t even notice until reading &#8220;<a href=\"https:\/\/www.goodreads.com\/book\/show\/12508475-the-c-programming-language\" target=\"_blank\" rel=\"noopener\">The C++ programming language<\/a>&#8221; by Barne Strousup. In the book the facility is presented as a concise template that is easy to use and even to implement. To understand it&#8217;s value, let&#8217;s start with an example. Suppose, I want to implement a template function to stream the contents of containers to stdout.<\/p>\n<pre class=\"brush: cpp; gutter: false; first-line: 1\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n#include &lt;list&gt;\n\ntemplate&lt;class ContainerT, class StreamT&gt;\nStreamT&amp; operator&lt;&lt;(StreamT&amp; strm, const ContainerT&amp; cont)\n{\n\tstrm &lt;&lt; '{';\n\tfor(const auto&amp; element : cont)\n\t\tstrm &lt;&lt; element &lt;&lt; \" \";\n\tstrm &lt;&lt; \"} \";\n        return strm;\n}\n\nint main()\n{\n\tstd::vector&lt;int&gt; ints{8, 45, 87, 90, 99999};\n\tstd::list&lt;float&gt; floats{3.14159, 2.71828, 0.57721, 1.618033};\n\tstd::cout &lt;&lt; ints &lt;&lt; floats;\n\n\treturn 0;\n}<\/pre>\n<p>So far so good, this does the trick. And the output is just what we expected: <em>{8 45 87 90 99999 } {3.14159 2.71828 0.57721 1.61803 }<\/em> But now we also write an output stream operator for some user defined interface type.<!--more--><\/p>\n<pre class=\"brush: cpp; gutter: false; first-line: 1\">#include &lt;iostream&gt;\n#include &lt;sstream&gt;\n#include &lt;vector&gt;\n#include &lt;list&gt;\n\ntemplate&lt;class ContainerT, class StreamT&gt;\nStreamT&amp; operator&lt;&lt;(StreamT&amp; strm, const ContainerT&amp; cont)\n{\n\tstrm &lt;&lt; '{';\n\tfor(const auto&amp; element : cont)\n\t\tstrm &lt;&lt; element &lt;&lt; \" \";\n\tstrm &lt;&lt; \"} \";\n        return strm;\n}\n\nstruct Base\n{\n    virtual std::string Describe() const = 0;\n};\n\nstd::ostream&amp; operator&lt;&lt;(std::ostream&amp; strm, const Base&amp; base)\n{\n    strm &lt;&lt; base.Describe() &lt;&lt; \" \";\n}\n\nstruct SomeType : public Base\n{\n    SomeType(int p1, int p2) : one_(p1), another_(p2) { }\n\n    virtual std::string Describe() const\n    {\n        std::stringstream sstr;\n        sstr &lt;&lt; \"{\" &lt;&lt; one_ &lt;&lt; \" \" &lt;&lt; another_ &lt;&lt; \"}\";\n        return sstr.str();\n    }\n\n\tint one_;\n\tint another_;\n};\n\nint main()\n{\n\tstd::vector&lt;int&gt; ints{8, 45, 87, 90, 99999};\n\tstd::list&lt;float&gt; floats{3.14159, 2.71828, 0.57721, 1.618033};\n\tstd::cout &lt;&lt; ints &lt;&lt; floats;\n\n\tSomeType some{456, 789};\n\tstd::cout &lt;&lt; some;\n\n\treturn 0;\n}<\/pre>\n<p>Looks inconspicuous, and should work. Right? What do you think happens?<\/p>\n<pre class=\"brush: bash; gutter: false; first-line: 1\">\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp: In Instanziierung von \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Fehler: keine passende Funktion f\u00c3\u00bcr Aufruf von \u00c2\u00bbbegin(const int&amp;)\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung: Kandidaten sind:\nIn file included from \/usr\/include\/c++\/4.7\/string:53:0,\n                 from \/usr\/include\/c++\/4.7\/bits\/locale_classes.h:42,\n                 from \/usr\/include\/c++\/4.7\/bits\/ios_base.h:43,\n                 from \/usr\/include\/c++\/4.7\/ios:43,\n                 from \/usr\/include\/c++\/4.7\/ostream:40,\n                 from \/usr\/include\/c++\/4.7\/iterator:64,\n                 from \/usr\/include\/boost\/iterator.hpp:17,\n                 from \/usr\/include\/boost\/concept_check.hpp:22,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:87:5: Anmerkung: template&lt;class _Tp, long unsigned int _Nm&gt; _Tp* std::begin(_Tp (&amp;)[_Nm])\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:87:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung:   unpassende Typen \u00c2\u00bb_Tp [_Nm]\u00c2\u00ab und \u00c2\u00bbconst int\u00c2\u00ab\nIn file included from \/usr\/include\/c++\/4.7\/string:53:0,\n                 from \/usr\/include\/c++\/4.7\/bits\/locale_classes.h:42,\n                 from \/usr\/include\/c++\/4.7\/bits\/ios_base.h:43,\n                 from \/usr\/include\/c++\/4.7\/ios:43,\n                 from \/usr\/include\/c++\/4.7\/ostream:40,\n                 from \/usr\/include\/c++\/4.7\/iterator:64,\n                 from \/usr\/include\/boost\/iterator.hpp:17,\n                 from \/usr\/include\/boost\/concept_check.hpp:22,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:58:5: Anmerkung: template&lt;class _Container&gt; decltype (__cont.begin()) std::begin(const _Container&amp;)\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:58:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h: In Ersetzung von \u00c2\u00bbtemplate&lt;class _Container&gt; decltype (__cont.begin()) std::begin(const _Container&amp;) [mit _Container = int]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2:   erfordert durch \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:58:5: Fehler: Abfrage des Elementes \u00c2\u00bbbegin\u00c2\u00ab in \u00c2\u00bb__cont\u00c2\u00ab, das vom Nicht-Klassentyp \u00c2\u00bbconst int\u00c2\u00ab ist\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp: In Instanziierung von \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:48:5: Anmerkung: template&lt;class _Container&gt; decltype (__cont.begin()) std::begin(_Container&amp;)\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:48:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h: In Ersetzung von \u00c2\u00bbtemplate&lt;class _Container&gt; decltype (__cont.begin()) std::begin(_Container&amp;) [mit _Container = const int]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2:   erfordert durch \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:48:5: Fehler: Abfrage des Elementes \u00c2\u00bbbegin\u00c2\u00ab in \u00c2\u00bb__cont\u00c2\u00ab, das vom Nicht-Klassentyp \u00c2\u00bbconst int\u00c2\u00ab ist\nIn file included from \/usr\/include\/c++\/4.7\/utility:76:0,\n                 from \/usr\/include\/boost\/config\/no_tr1\/utility.hpp:21,\n                 from \/usr\/include\/boost\/config\/select_stdlib_config.hpp:37,\n                 from \/usr\/include\/boost\/config.hpp:40,\n                 from \/usr\/include\/boost\/concept\/assert.hpp:7,\n                 from \/usr\/include\/boost\/concept_check.hpp:20,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp: In Instanziierung von \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/initializer_list:89:5: Anmerkung: template&lt;class _Tp&gt; constexpr const _Tp* std::begin(std::initializer_list&lt;_Tp&gt;)\n\/usr\/include\/c++\/4.7\/initializer_list:89:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung:   unpassende Typen \u00c2\u00bbstd::initializer_list&lt;_Tp&gt;\u00c2\u00ab und \u00c2\u00bbint\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Fehler: keine passende Funktion f\u00c3\u00bcr Aufruf von \u00c2\u00bbend(const int&amp;)\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung: Kandidaten sind:\nIn file included from \/usr\/include\/c++\/4.7\/string:53:0,\n                 from \/usr\/include\/c++\/4.7\/bits\/locale_classes.h:42,\n                 from \/usr\/include\/c++\/4.7\/bits\/ios_base.h:43,\n                 from \/usr\/include\/c++\/4.7\/ios:43,\n                 from \/usr\/include\/c++\/4.7\/ostream:40,\n                 from \/usr\/include\/c++\/4.7\/iterator:64,\n                 from \/usr\/include\/boost\/iterator.hpp:17,\n                 from \/usr\/include\/boost\/concept_check.hpp:22,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:97:5: Anmerkung: template&lt;class _Tp, long unsigned int _Nm&gt; _Tp* std::end(_Tp (&amp;)[_Nm])\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:97:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung:   unpassende Typen \u00c2\u00bb_Tp [_Nm]\u00c2\u00ab und \u00c2\u00bbconst int\u00c2\u00ab\nIn file included from \/usr\/include\/c++\/4.7\/string:53:0,\n                 from \/usr\/include\/c++\/4.7\/bits\/locale_classes.h:42,\n                 from \/usr\/include\/c++\/4.7\/bits\/ios_base.h:43,\n                 from \/usr\/include\/c++\/4.7\/ios:43,\n                 from \/usr\/include\/c++\/4.7\/ostream:40,\n                 from \/usr\/include\/c++\/4.7\/iterator:64,\n                 from \/usr\/include\/boost\/iterator.hpp:17,\n                 from \/usr\/include\/boost\/concept_check.hpp:22,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:78:5: Anmerkung: template&lt;class _Container&gt; decltype (__cont.end()) std::end(const _Container&amp;)\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:78:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h: In Ersetzung von \u00c2\u00bbtemplate&lt;class _Container&gt; decltype (__cont.end()) std::end(const _Container&amp;) [mit _Container = int]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2:   erfordert durch \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:78:5: Fehler: Abfrage des Elementes \u00c2\u00bbend\u00c2\u00ab in \u00c2\u00bb__cont\u00c2\u00ab, das vom Nicht-Klassentyp \u00c2\u00bbconst int\u00c2\u00ab ist\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp: In Instanziierung von \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:68:5: Anmerkung: template&lt;class _Container&gt; decltype (__cont.end()) std::end(_Container&amp;)\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:68:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h: In Ersetzung von \u00c2\u00bbtemplate&lt;class _Container&gt; decltype (__cont.end()) std::end(_Container&amp;) [mit _Container = const int]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2:   erfordert durch \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:68:5: Fehler: Abfrage des Elementes \u00c2\u00bbend\u00c2\u00ab in \u00c2\u00bb__cont\u00c2\u00ab, das vom Nicht-Klassentyp \u00c2\u00bbconst int\u00c2\u00ab ist\nIn file included from \/usr\/include\/c++\/4.7\/utility:76:0,\n                 from \/usr\/include\/boost\/config\/no_tr1\/utility.hpp:21,\n                 from \/usr\/include\/boost\/config\/select_stdlib_config.hpp:37,\n                 from \/usr\/include\/boost\/config.hpp:40,\n                 from \/usr\/include\/boost\/concept\/assert.hpp:7,\n                 from \/usr\/include\/boost\/concept_check.hpp:20,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp: In Instanziierung von \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = int; StreamT = std::basic_stringstream&lt;char&gt;]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:34:24:   von hier erfordert\n\/usr\/include\/c++\/4.7\/initializer_list:99:5: Anmerkung: template&lt;class _Tp&gt; constexpr const _Tp* std::end(std::initializer_list&lt;_Tp&gt;)\n\/usr\/include\/c++\/4.7\/initializer_list:99:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung:   unpassende Typen \u00c2\u00bbstd::initializer_list&lt;_Tp&gt;\u00c2\u00ab und \u00c2\u00bbint\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Fehler: \u00c2\u00bbconst auto&amp;\u00c2\u00ab kann nicht aus \u00c2\u00bb&lt;Ausdrucksfehler&gt;\u00c2\u00ab hergeleitet werden\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp: In Instanziierung von \u00c2\u00bbStreamT&amp; operator&lt;&lt;(StreamT&amp;, const ContainerT&amp;) [mit ContainerT = SomeType; StreamT = std::basic_ostream&lt;char&gt;]\u00c2\u00ab:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:49:15:   von hier erfordert\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Fehler: keine passende Funktion f\u00c3\u00bcr Aufruf von \u00c2\u00bbbegin(const SomeType&amp;)\u00c2\u00ab\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung: Kandidaten sind:\nIn file included from \/usr\/include\/c++\/4.7\/string:53:0,\n                 from \/usr\/include\/c++\/4.7\/bits\/locale_classes.h:42,\n                 from \/usr\/include\/c++\/4.7\/bits\/ios_base.h:43,\n                 from \/usr\/include\/c++\/4.7\/ios:43,\n                 from \/usr\/include\/c++\/4.7\/ostream:40,\n                 from \/usr\/include\/c++\/4.7\/iterator:64,\n                 from \/usr\/include\/boost\/iterator.hpp:17,\n                 from \/usr\/include\/boost\/concept_check.hpp:22,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:87:5: Anmerkung: template&lt;class _Tp, long unsigned int _Nm&gt; _Tp* std::begin(_Tp (&amp;)[_Nm])\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:87:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:\n\/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:12:2: Anmerkung:   unpassende Typen \u00c2\u00bb_Tp [_Nm]\u00c2\u00ab und \u00c2\u00bbconst SomeType\u00c2\u00ab\nIn file included from \/usr\/include\/c++\/4.7\/string:53:0,\n                 from \/usr\/include\/c++\/4.7\/bits\/locale_classes.h:42,\n                 from \/usr\/include\/c++\/4.7\/bits\/ios_base.h:43,\n                 from \/usr\/include\/c++\/4.7\/ios:43,\n                 from \/usr\/include\/c++\/4.7\/ostream:40,\n                 from \/usr\/include\/c++\/4.7\/iterator:64,\n                 from \/usr\/include\/boost\/iterator.hpp:17,\n                 from \/usr\/include\/boost\/concept_check.hpp:22,\n                 from \/usr\/include\/boost\/range\/concepts.hpp:19,\n                 from \/home\/richi\/sourcecode\/experiments\/enable_if\/enable_if_test.cpp:1:\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:58:5: Anmerkung: template&lt;class _Container&gt; decltype (__cont.begin()) std::begin(const _Container&amp;)\n\/usr\/include\/c++\/4.7\/bits\/range_access.h:58:5: Anmerkung:   Herleitung\/Ersetzung von Templateargument gescheitert:<\/pre>\n<p>Wow, that was a mouth full. Because the overload types don&#8217;t match exactly, the template is considered, which in turn fails inside. What we need, is a way to tell the compiler that this template function should not be considered for types that aren&#8217;t containers. Too bad, the language has no direct support for that. So we are left with having to do some trickery. We could do this with tag dispatching and type traits. Tag dispatching is usually a technique to emulate partial template specialization for function templates. But for stuff like that, it&#8217;s quite helpful, if overly verbose and distracting. I wont show this technique here, as I think it&#8217;s better suited for different stuff. Instead I present the solution with enable_if. It works thanks to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Substitution_failure_is_not_an_error\">SFINAE (substitution failure is not an error)<\/a>. By not providing a valid type at all, the function is removed from the set of possible overloads.<\/p>\n<pre class=\"brush: cpp; gutter: false; first-line: 1\">#include &lt;boost\/range\/concepts.hpp&gt;\n#include &lt;type_traits&gt;\n#include &lt;iostream&gt;\n#include &lt;sstream&gt;\n#include &lt;vector&gt;\n#include &lt;list&gt;\n#include &lt;iterator&gt;\n\ntemplate &lt;typename T&gt;\nstruct sfinae_true : std::true_type {};\n\nstruct is_iterator_tester {\n    template &lt;typename T&gt;\n    static sfinae_true&lt;typename std::iterator_traits&lt;T&gt;::iterator_category&gt; test(int);\n\n    template &lt;typename&gt;\n    static std::false_type test(...);\n};\n\ntemplate &lt;typename T&gt;\nstruct is_iterator : decltype(is_iterator_tester::test&lt;T&gt;(0)) {};\n\ntemplate&lt;class ContainerT, class StreamT,\n  typename enabler = typename std::enable_if&lt;is_iterator&lt;\n    decltype(std::begin(*static_cast&lt;ContainerT*&gt;(nullptr)))&gt;::value,\n  ContainerT&gt;::type&gt;\nStreamT&amp; operator&lt;&lt;(StreamT&amp; strm, const ContainerT&amp; cont)\n{\n\tstrm &lt;&lt; '{';\n\tfor(const auto&amp; element : cont)\n\t\tstrm &lt;&lt; element &lt;&lt; ' ';\n\tstrm &lt;&lt; '}';\n\n\treturn strm;\n}\n\nstruct Base\n{\n    virtual std::string Describe() const = 0;\n};\n\nstd::ostream&amp; operator&lt;&lt;(std::ostream&amp; strm, const Base&amp; base)\n{\n    strm &lt;&lt; base.Describe() &lt;&lt; \" \";\n}\n\nstruct SomeType : public Base\n{\n    SomeType(int p1, int p2) : one_(p1), another_(p2) { }\n\n    virtual std::string Describe() const\n    {\n        std::stringstream sstr;\n        sstr &lt;&lt; '{' &lt;&lt; one_ &lt;&lt; ' ' &lt;&lt; another_ &lt;&lt; '}';\n        return sstr.str();\n    }\n\n\tint one_;\n\tint another_;\n};\n\nint main()\n{\n\tstd::vector&lt;int&gt; ints{8, 45, 87, 90, 99999};\n\tstd::list&lt;float&gt; floats{3.14159, 2.71828, 0.57721, 1.618033};\n\tstd::cout &lt;&lt; ints &lt;&lt; floats;\n\n\tSomeType some{456, 789};\n\tstd::cout &lt;&lt; some;\n\n\treturn 0;\n}<\/pre>\n<p>You can find the <a href=\"https:\/\/github.com\/ulrichard\/experiments\/tree\/master\/enable_if\" target=\"_blank\" rel=\"noopener\">full source code at github<\/a>. I&#8217;m sure this could still be improved a lot. It&#8217;s not really easy to read. Probably boost::range::concepts could be of help. But I wanted an implementation which only uses the standard library. The is_iterator stuff was copied from <a href=\"http:\/\/flamingdangerzone.com\/cxx11\/2012\/06\/01\/almost-static-if.html\">Flaming Danger Zone<\/a>.\u00c2\u00a0 I crafted the code for this blog post, and did not yet use it in production code. It would probably still need some tweaking.<\/p>\n<p>For completeness, here is an implementation of enable_if itself. Considering all the above code, it is remarkably simple.<\/p>\n<pre class=\"brush: cpp; gutter: false; first-line: 1\">struct enable_if {}; \/\/ no members\ntemplate &lt;bool Condition, typename T = void&gt;\n\ntemplate &lt;typename T&gt;\nstruct enable_if&lt;true, T&gt; { using type = T; }; \/\/ type member<\/pre>\n<p>To conclude, I must admit, even the solution with enable_if could be a lot nicer. <a href=\"http:\/\/channel9.msdn.com\/Events\/GoingNative\/GoingNative-2012\/Static-If-I-Had-a-Hammer\">And there might be already something in the works<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It was roughly 2008, when I wanted to make a template function for serialization, only available to container types. Template stuff can become complicated at times, and from reading the documentation boost::enable_if seemed to be just what I needed. I didn&#8217;t get it to work, and I blamed Microsoft Visual Studio 2005 for not being [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,1],"tags":[43],"class_list":["post-1151","post","type-post","status-publish","format-standard","hentry","category-software","category-uncategorized","tag-c"],"_links":{"self":[{"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1151","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1151"}],"version-history":[{"count":0,"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1151\/revisions"}],"wp:attachment":[{"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1151"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ulrichard.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}