From 50163009b13386d21c80e38759db2b4818cde7b0 Mon Sep 17 00:00:00 2001 From: John McCall 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 | 152 ++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/www/cxx_compatibility.html b/www/cxx_compatibility.html index 5ad443fc43..1273ed3a8c 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.

Some versions of GCC accept the following invalid code:

-#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);
+}
+
+ +

Clang complains: + +

  my_file.cpp:2:10: error: use of undeclared identifier 'Multiply'
+    return Multiply(x, x);
+           ^
+
+  my_file.cpp:10:3: note: in instantiation of function template specialization 'Squared<int>' requested here
+    Squared(5);
+    ^
+
+ +

The C++ standard says that unqualified names like Multiply +are looked up in two ways. + +

First, the compiler does unqualified lookup 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 Multiply hasn't been declared yet at +this point, unqualified lookup won't find it. + +

Second, if the name is called like a function, then the compiler +also does argument-dependent lookup (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. + +

In our example, Multiply is called with dependent +arguments, so ADL isn't done until the template is instantiated. At +that point, the arguments both have type int, 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 Multiply, the code doesn't compile. + +

Here's another example, this time using overloaded operators, +which obey very similar rules. + +

#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 << ")"; -} - - -

Clang complains:

+

Again, Clang complains about not finding a matching function:

-test.cc:6:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> const')
+my_file.cpp:5:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')
   std::cout << value << "\n";
-  ~~~~~~~~~ ^  ~~~~~
-test.cc:18:3: note: in instantiation of function template specialization 'Dump<std::pair<int, double> >' requested here
-  Dump(std::make_pair(3, 4.5));
-  ^
-test.cc:6:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')
-  std::cout << value << "\n";
-  ~~~~~~~~~ ^  ~~~~~
-test.cc:19:3: note: in instantiation of function template specialization 'Dump<ns::Data>' requested here
+  ~~~~~~~~~ ^  ~~~~~
+my_file.cpp:17:3: note: in instantiation of function template specialization 'Dump<ns::Data>' requested here
   Dump(ns::Data());
-  ^
-2 errors generated.
+  ^
 
-

The standard, in [temp.dep.candidate], says that unqualified names -like operator<< are looked up when the template is -defined, not when it's instantiated. Since -operator<<(std::ostream&, const std::pair<>&) -and operator<<(std::ostream&, ns::Data) were not -declared yet when Dump was defined, they're not considered. +

Just like before, unqualified lookup didn't find any declarations +with the name operator<<. Unlike before, the argument +types both contain class types: one of them is an instance of the +class template type std::basic_ostream, and the other is the +type ns::Data that we declared above. Therefore, ADL will +look in the namespaces std and ns for +an operator<<. Since one of the argument types was +still dependent during the template definition, ADL isn't done until +the template is instantiated during Use, which means that +the operator<< 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! -

This is complicated by argument-dependent lookup (ADL), -which is done when unqualified names are called as functions, -like operator<< above. The standard says that ADL is -performed in both places if any of the arguments are type-dependent, -like value and p are in this example. - -

The fix is usually to

-
  1. Add a declaration before the use of the function, -
  2. Move the definition to before the use of the function, or +

    There are two ways to fix this problem:

    +
    1. 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.
    2. 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 instantiated, and that ADL doesn't apply to -built-in types.) -
    +so that ADL applies.
-
-#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());
-}
-
+

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].

Unqualified lookup into dependent bases of class templates