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
-There are two ways to fix this problem:
+-#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].