Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Since it does not seem that this question is getting closed as exact duplicate, I will explain what your program does.</p> <pre><code>template &lt;typename T&gt; class test { int private_field; friend std::ostream&amp; operator&lt;&lt;( std::ostream&amp;, test&lt;T&gt; const &amp; ); }; // ??? int main() { std::cout &lt;&lt; test&lt;int&gt;() &lt;&lt; std::endl; } </code></pre> <p>Templates are instantiated on demand (unless you explicitly instantiate them), that means that in this particular program <code>test</code> is instantiated only as <code>test&lt;int&gt;</code>. When the compiler instantiates the template (because it was requested in <code>main</code>) it will process the template definition. At this point the behavior is similar to rewriting the code with the substituted type at the point of definition (in this case right before <code>main</code>):</p> <pre><code>class test&lt;int&gt; { friend std::ostream&amp; operator&lt;&lt;( std::ostream&amp;, test&lt;int&gt; const &amp; ); }; </code></pre> <p>Now, if you look at the instantiated template, you can note that the <code>friend</code> declaration is befriending a non templated function. So in this particular program you can fill in the <code>???</code> with that particular function:</p> <pre><code>std::ostream&amp; operator&lt;&lt;( std::ostream&amp; o, test&lt;int&gt; const &amp; t ) { return o &lt;&lt; t.private_field; } </code></pre> <p>The problem here is that this is not easily extensible. The code of that <code>operator&lt;&lt;</code> is the same for <code>test&lt;int&gt;</code> than it is for <code>test&lt;double&gt;</code>, so there should be no need to <em>rewrite</em> the same function for all instantiating types!</p> <p>At this point there are two options, the first one is, as you have already identified, providing the definition of the function inside the class. The compiler will then process and define the function whenever the type is being instantiated. This solution creates non-templated functions on demand for each template instantiation (which is the option I would do, even with the quirks and strangeness of lookup here).</p> <p>Now, if you really want to avoid providing the definition inside the templates class, and you still want to provide a single implementation, then you have to resort to providing a templates <code>operator&lt;&lt;</code>. At this point you have two different options, you can declare <em>all</em> instantiations of the template (which I don't quite like, as it opens to too many others), or you can befriend a single specialization of the template function (cleaner regarding access, more cumbersome to write).</p> <p>The first case is:</p> <pre><code>template &lt;typename T&gt; class test { template &lt;typename U&gt; friend std::ostream&amp; operator&lt;&lt;( std::ostream&amp;, test&lt;U&gt; const &amp; ); }; template &lt;typename T&gt; std::ostream&amp; operator&lt;&lt;( std::ostream&amp;, test&lt;T&gt; const &amp; ) { ... } </code></pre> <p>The second case requires a couple of forward declarations:</p> <pre><code>template &lt;typename T&gt; test; template &lt;typename T&gt; std::ostream&amp; operator&lt;&lt;( std::ostream&amp;, test&lt;T&gt; const &amp; ); template &lt;typename T&gt; class test { friend std::ostream&amp; operator&lt;&lt; &lt;T&gt;( std::ostream&amp;, const test&lt;T&gt;&amp; ); }; template &lt;typename T&gt; std::ostream&amp; operator&lt;&lt;( std::ostream&amp;, test&lt;T&gt; const &amp; ) { ... } </code></pre> <p>There is of course another option: do not declare <code>friend</code>s at all. Provide a <code>print(std::ostream&amp;)</code> public function in your class that has the implementation, and provide a non-templated <code>operator&lt;&lt;</code> that just calls <code>print</code> on the second argument.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload