From 50163009b13386d21c80e38759db2b4818cde7b0 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 16 Jun 2010 10:48:16 +0000 Subject: [PATCH] Rework the unqualified-lookup-in-templates section of the compatibility document. jyasskin, let me know if this meets your needs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106098 91177308-0d34-0410-b5e6-96231b3b80d8 --- www/cxx_compatibility.html | 154 ++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 70 deletions(-) diff --git a/www/cxx_compatibility.html b/www/cxx_compatibility.html index 5ad443fc433..1273ed3a8c3 100644 --- a/www/cxx_compatibility.html +++ b/www/cxx_compatibility.html @@ -120,8 +120,60 @@ Note that the forthcoming C++0x standard will allow this. <p>Some versions of GCC accept the following invalid code: <pre> -#include <iostream> -#include <utility> +template <typename T> T Squared(T x) { + return Multiply(x, x); +} + +int Multiply(int x, int y) { + return x * y; +} + +int main() { + Squared(5); +} +</pre> + +<p>Clang complains: + +<pre> <b>my_file.cpp:2:10: <span class="error">error:</span> use of undeclared identifier 'Multiply'</b> + return Multiply(x, x); + <span class="caret"> ^</span> + + <b>my_file.cpp:10:3: <span class="note">note:</span> in instantiation of function template specialization 'Squared<int>' requested here</b> + Squared(5); + <span class="caret"> ^</span> +</pre> + +<p>The C++ standard says that unqualified names like <q>Multiply</q> +are looked up in two ways. + +<p>First, the compiler does <i>unqualified lookup</i> in the scope +where the name was written. For a template, this means the lookup is +done at the point where the template is defined, not where it's +instantiated. Since <tt>Multiply</tt> hasn't been declared yet at +this point, unqualified lookup won't find it. + +<p>Second, if the name is called like a function, then the compiler +also does <i>argument-dependent lookup</i> (ADL). (Sometimes +unqualified lookup can suppress ADL; see [basic.lookup.argdep]p3 for +more information.) In ADL, the compiler looks at the types of all the +arguments to the call. When it finds a class type, it looks up the +name in that class's namespace; the result is all the declarations it +finds in those namespaces, plus the declarations from unqualified +lookup. However, the compiler doesn't do ADL until it knows all the +argument types. + +<p>In our example, <tt>Multiply</tt> is called with dependent +arguments, so ADL isn't done until the template is instantiated. At +that point, the arguments both have type <tt>int</tt>, which doesn't +contain any class types, and so ADL doesn't look in any namespaces. +Since neither form of lookup found the declaration +of <tt>Multiply</tt>, the code doesn't compile. + +<p>Here's another example, this time using overloaded operators, +which obey very similar rules. + +<pre>#include <iostream> template<typename T> void Dump(const T& value) { @@ -132,88 +184,50 @@ namespace ns { struct Data {}; } -std::ostream& operator<<(std::ostream& out, ns::Data) { +std::ostream& operator<<(std::ostream& out, ns::Data data) { return out << "Some data"; } void Use() { - Dump(std::make_pair(3, 4.5)); Dump(ns::Data()); -} - -template<typename T, typename U> -std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& p) { - return out << '(' << p.first << ", " << p.second << ")"; -} -</pre> +}</pre> -<p>Clang complains:</p> +<p>Again, Clang complains about not finding a matching function:</p> <pre> -<b>test.cc:6:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> const')</b> +<b>my_file.cpp:5:13: <span class="error">error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')</b> std::cout << value << "\n"; - <span class=caret>~~~~~~~~~ ^ ~~~~~</span> -<b>test.cc:18:3: note:</b> in instantiation of function template specialization 'Dump<std::pair<int, double> >' requested here - Dump(std::make_pair(3, 4.5)); - <span class=caret>^</span> -<b>test.cc:6:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')</b> - std::cout << value << "\n"; - <span class=caret>~~~~~~~~~ ^ ~~~~~</span> -<b>test.cc:19:3: note:</b> in instantiation of function template specialization 'Dump<ns::Data>' requested here + <span class="caret">~~~~~~~~~ ^ ~~~~~</span> +<b>my_file.cpp:17:3: <span class="note">note:</span> in instantiation of function template specialization 'Dump<ns::Data>' requested here</b> Dump(ns::Data()); - <span class=caret>^</span> -2 errors generated. + <span class="caret">^</span> </pre> -<p>The standard, in [temp.dep.candidate], says that unqualified names -like <tt>operator<<</tt> are looked up when the template is -defined, not when it's instantiated. Since -<tt>operator<<(std::ostream&, const std::pair<>&)</tt> -and <tt>operator<<(std::ostream&, ns::Data)</tt> were not -declared yet when <tt>Dump</tt> was defined, they're not considered. - -<p>This is complicated by <i>argument-dependent lookup</i> (ADL), -which is done when unqualified names are called as functions, -like <tt>operator<<</tt> above. The standard says that ADL is -performed in both places if any of the arguments are type-dependent, -like <tt>value</tt> and <tt>p</tt> are in this example. - -<p>The fix is usually to</p> -<ol><li>Add a declaration before the use of the function, -<li>Move the definition to before the use of the function, or +<p>Just like before, unqualified lookup didn't find any declarations +with the name <tt>operator<<</tt>. Unlike before, the argument +types both contain class types: one of them is an instance of the +class template type <tt>std::basic_ostream</tt>, and the other is the +type <tt>ns::Data</tt> that we declared above. Therefore, ADL will +look in the namespaces <tt>std</tt> and <tt>ns</tt> for +an <tt>operator<<</tt>. Since one of the argument types was +still dependent during the template definition, ADL isn't done until +the template is instantiated during <tt>Use</tt>, which means that +the <tt>operator<<</tt> we want it to find has already been +declared. Unfortunately, it was declared in the global namespace, not +in either of the namespaces that ADL will look in! + +<p>There are two ways to fix this problem:</p> +<ol><li>Make sure the function you want to call is declared before the +template that might call it. This is the only option if none of its +argument types contain classes. You can do this either by moving the +template definition, or by moving the function definition, or by +adding a forward declaration of the function before the template.</li> <li>Move the function into the same namespace as one of its arguments -so that ADL applies. (Note that it still needs to be declared before -the template is <i>instantiated</i>, and that ADL doesn't apply to -built-in types.) -</ol> +so that ADL applies.</li></ol> -<pre> -#include <iostream> -#include <utility> - -template<typename T, typename U> // Fix 2 -std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& p) { - return out << '(' << p.first << ", " << p.second << ")"; -} - -template<typename T> -void Dump(const T& value) { - std::cout << value << "\n"; -} - -namespace ns { - struct Data {}; - - std::ostream& operator<<(std::ostream& out, Data) { // Fix 3 - return out << "Some data"; - } -} - -void Use() { - Dump(std::make_pair(3, 4.5)); - Dump(ns::Data()); -} -</pre> +<p>For more information about argument-dependent lookup, see +[basic.lookup.argdep]. For more information about the ordering of +lookup in templates, see [temp.dep.candidate]. <!-- ======================================================================= --> <h2 id="dep_lookup_bases">Unqualified lookup into dependent bases of class templates</h2> -- GitLab