Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<h2>C++11</h2> <h3>Problem</h3> <p>While the rules in C++03 about when you need <code>typename</code> and <code>template</code> are largely reasonable, there is one annoying disadvantage of its formulation</p> <pre><code>template&lt;typename T&gt; struct A { typedef int result_type; void f() { // error, "this" is dependent, "template" keyword needed this-&gt;g&lt;float&gt;(); // OK g&lt;float&gt;(); // error, "A&lt;T&gt;" is dependent, "typename" keyword needed A&lt;T&gt;::result_type n1; // OK result_type n2; } template&lt;typename U&gt; void g(); }; </code></pre> <p>As can be seen, we need the disambiguation keyword even if the compiler could perfectly figure out itself that <code>A::result_type</code> can only be <code>int</code> (and is hence a type), and <code>this-&gt;g</code> can only be the member template <code>g</code> declared later (even if <code>A</code> is explicitly specialized somewhere, that would not affect the code within that template, so its meaning cannot be affected by a later specialization of <code>A</code>!). </p> <h3>Current instantiation</h3> <p>To improve the situation, in C++11 the language tracks when a type refers to the enclosing template. To know that, the type must have been formed by using a certain form of name, which is its own name (in the above, <code>A</code>, <code>A&lt;T&gt;</code>, <code>::A&lt;T&gt;</code>). A type referenced by such a name is known to be the <em>current instantiation</em>. There may be multiple types that are all the current instantiation if the type from which the name is formed is a member/nested class (then, <code>A::NestedClass</code> and <code>A</code> are both current instantiations). </p> <p>Based on this notion, the language says that <code>CurrentInstantiation::Foo</code>, <code>Foo</code> and <code>CurrentInstantiationTyped-&gt;Foo</code> (such as <code>A *a = this; a-&gt;Foo</code>) are all <em>member of the current instantiation</em> <strong>if</strong> they are found to be members of a class that is the current instantiation or one of its non-dependent base classes (by just doing the name lookup immediately). </p> <p>The keywords <code>typename</code> and <code>template</code> are now not required anymore if the qualifier is a member of the current instantiation. A keypoint here to remember is that <code>A&lt;T&gt;</code> is <em>still</em> a type-dependent name (after all <code>T</code> is also type dependent). But <code>A&lt;T&gt;::result_type</code> is known to be a type - the compiler will "magically" look into this kind of dependent types to figure this out. </p> <pre><code>struct B { typedef int result_type; }; template&lt;typename T&gt; struct C { }; // could be specialized! template&lt;typename T&gt; struct D : B, C&lt;T&gt; { void f() { // OK, member of current instantiation! // A::result_type is not dependent: int D::result_type r1; // error, not a member of the current instantiation D::questionable_type r2; // OK for now - relying on C&lt;T&gt; to provide it // But not a member of the current instantiation typename D::questionable_type r3; } }; </code></pre> <p>That's impressive, but can we do better? The language even goes further and <em>requires</em> that an implementation again looks up <code>D::result_type</code> when instantiating <code>D::f</code> (even if it found its meaning already at definition time). When now the lookup result differs or results in ambiguity, the program is ill-formed and a diagnostic must be given. Imagine what happens if we defined <code>C</code> like this</p> <pre><code>template&lt;&gt; struct C&lt;int&gt; { typedef bool result_type; typedef int questionable_type; }; </code></pre> <p>A compiler is required to catch the error when instantiating <code>D&lt;int&gt;::f</code>. So you get the best of the two worlds: "Delayed" lookup protecting you if you could get in trouble with dependent base classes, and also "Immediate" lookup that frees you from <code>typename</code> and <code>template</code>. </p> <h3>Unknown specializations</h3> <p>In the code of <code>D</code>, the name <code>typename D::questionable_type</code> is not a member of the current instantiation. Instead the language marks it as a <em>member of an unknown specialization</em>. In particular, this is always the case when you are doing <code>DependentTypeName::Foo</code> or <code>DependentTypedName-&gt;Foo</code> and either the dependent type is <em>not</em> the current instantiation (in which case the compiler can give up and say "we will look later what <code>Foo</code> is) or it <em>is</em> the current instantiation and the name was not found in it or its non-dependent base classes and there are also dependent base classes. </p> <p>Imagine what happens if we had a member function <code>h</code> within the above defined <code>A</code> class template</p> <pre><code>void h() { typename A&lt;T&gt;::questionable_type x; } </code></pre> <p>In C++03, the language allowed to catch this error because there could never be a valid way to instantiate <code>A&lt;T&gt;::h</code> (whatever argument you give to <code>T</code>). In C++11, the language now has a further check to give more reason for compilers to implement this rule. Since <code>A</code> has no dependent base classes, and <code>A</code> declares no member <code>questionable_type</code>, the name <code>A&lt;T&gt;::questionable_type</code> is <em>neither</em> a member of the current instantiation <em>nor</em> a member of an unknown specialization. In that case, there should be no way that that code could validly compile at instantiation time, so the language forbids a name where the qualifier is the current instantiation to be neither a member of an unknown specialization nor a member of the current instantiation (however, this violation is still not required to be diagnosed).</p> <h3>Examples and trivia</h3> <p>You can try this knowledge on <a href="https://stackoverflow.com/a/14005063/34509">this answer</a> and see whether the above definitions make sense for you on a real-world example (they are repeated slightly less detailed in that answer). </p> <p>The C++11 rules make the following valid C++03 code ill-formed (which was not intended by the C++ committee, but will probably not be fixed)</p> <pre><code>struct B { void f(); }; struct A : virtual B { void f(); }; template&lt;typename T&gt; struct C : virtual B, T { void g() { this-&gt;f(); } }; int main() { C&lt;A&gt; c; c.g(); } </code></pre> <p>This valid C++03 code would bind <code>this-&gt;f</code> to <code>A::f</code> at instantiation time and everything is fine. C++11 however immediately binds it to <code>B::f</code> and requires a double-check when instantiating, checking whether the lookup still matches. However when instantiating <code>C&lt;A&gt;::g</code>, the <a href="http://en.wikipedia.org/wiki/Dominance_(C%2B%2B)" rel="noreferrer">Dominance Rule</a> applies and lookup will find <code>A::f</code> instead.</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