From: Jeffrey Yasskin Date: Wed, 16 Jun 2010 01:12:12 +0000 (+0000) Subject: Fix template ordering compatibility docs. I missed another section that covered X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f44ea727c0f632f9b6a262cf36dd5c9323c2467f;p=clang Fix template ordering compatibility docs. I missed another section that covered the same thing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106076 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/www/cxx_compatibility.html b/www/cxx_compatibility.html index 27defc2d8a..46e3a9f909 100644 --- a/www/cxx_compatibility.html +++ b/www/cxx_compatibility.html @@ -25,7 +25,6 @@
  • Initialization of non-integral static const data members within a class definition
  • Unqualified lookup in templates
  • Unqualified lookup into dependent bases of class templates
  • -
  • Template uses of a function must either find the function by ADL or come after the declaration of the function
  • Incomplete types in templates
  • Templates with no valid instantiations
  • Default initialization of const variable of a class type requires user-defined default constructor
  • @@ -118,33 +117,103 @@ Note that the forthcoming C++0x standard will allow this.

    Unqualified lookup in templates

    -Some versions of GCC accept the following invalid code: +

    Some versions of GCC accept the following invalid code:

    -template <typename T> struct Foo {
    -  void Work(T x) {
    -    func(x);
    -  }
    -};
    -...
    -void func(int x);
    -...
    -template struct Foo<int>; // or anything else that instantiates Foo<int>::Work
    +#include <iostream>
    +#include <utility>
    +
    +template<typename T>
    +void Dump(const T& value) {
    +  std::cout << value << "\n";
    +}
    +
    +namespace ns {
    +  struct Data {};
    +}
    +
    +std::ostream& operator<<(std::ostream& out, ns::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 << ")";
    +}
     
    -The standard says that unqualified names like func are looked up -when the template is defined, not when it's instantiated. Since -void func(int) was not declared yet when Foo was -defined, it's not considered. The fix is usually to -declare func before Foo. +

    Clang complains:

    + +
    +test.cc:6:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> 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
    +  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.

    This is complicated by argument-dependent lookup (ADL), which is done when unqualified names are called as functions, -like func(x) above. The standard says that ADL is performed -in both places if any of the arguments are type-dependent, like -x is in this example. However, ADL does nothing for builtin -types like int, so the example is still invalid. See -[basic.lookup.argdep] for more information. +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 +
    3. 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.) +
    + +
    +#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());
    +}
    +

    Unqualified lookup into dependent bases of class templates

    @@ -216,129 +285,6 @@ This works whether the methods are static or not, but be careful: if DoThis is virtual, calling it this way will bypass virtual dispatch! - -

    Template uses of a function must either find the function by ADL or come after the declaration of the function

    - - -

    For example, gcc-4.4 accepts the following code:

    - -
    -#include <iostream>
    -#include <utility>
    -#include <vector>
    -
    -template<typename T>
    -void Dump(const T& value) {
    -  std::cout << value << "\n";
    -}
    -
    -template<typename T, typename U>
    -std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& i) {
    -  return out << '(' << i.first << ", " << i.second << ")";
    -}
    -
    -namespace ns {
    -  struct Data {};
    -}
    -
    -std::ostream& operator<<(std::ostream& out, ns::Data) {
    -  return out << "Some data";
    -}
    -
    -void Use() {
    -  Dump(std::make_pair(3, 4.5));
    -  Dump(ns::Data());
    -  Dump(std::vector<const char*>(1, "Hello World"));
    -}
    -
    -template<typename T>
    -std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec) {
    -  out << '[';
    -  for (size_t i = 0, size = vec.size(); i != size; ++i) {
    -    if (i != 0)
    -      out << ", ";
    -    out << vec[i];
    -  }
    -  return out << ']';
    -}
    -
    - -

    while clang, following the rules in [temp.dep.candidate] -complains:

    - -
    -test.cc:7:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> const')
    -  std::cout << value << "\n";
    -  ~~~~~~~~~ ^  ~~~~~
    -test.cc:24:3: note: in instantiation of function template specialization 'Dump<std::pair<int, double> >' requested here
    -  Dump(std::make_pair(3, 4.5));
    -  ^
    -test.cc:7:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')
    -  std::cout << value << "\n";
    -  ~~~~~~~~~ ^  ~~~~~
    -test.cc:25:3: note: in instantiation of function template specialization 'Dump<ns::Data>' requested here
    -  Dump(ns::Data());
    -  ^
    -test.cc:7:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::vector<char const *, std::allocator<char const *> > const')
    -  std::cout << value << "\n";
    -  ~~~~~~~~~ ^  ~~~~~
    -test.cc:26:3: note: in instantiation of function template specialization 'Dump<std::vector<char const *, std::allocator<char const *> > >' requested here
    -  Dump(std::vector<const char*>(1, "Hello World"));
    -  ^
    -3 errors generated.
    -
    - -

    The fix is to

    -
    1. Add a declaration before the use of the function, or -
    2. Move the definition to before the use of the function, or -
    3. Move the function into the same namespace as one of its -arguments. (Note that it still needs to be declared before the -template is instantiated.) -
    - -
    -#include <iostream>
    -#include <utility>
    -#include <vector>
    -
    -template<typename T>  // Fix 1.
    -std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec);
    -
    -template<typename T, typename U>  // Fix 2.
    -std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& i) {
    -  return out << '(' << i.first << ", " << i.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());
    -  Dump(std::vector<const char*>(1, "Hello World"));
    -}
    -
    -template<typename T>
    -std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec) {
    -  out << '[';
    -  for (size_t i = 0, size = vec.size(); i != size; ++i) {
    -    if (i != 0)
    -      out << ", ";
    -    out << vec[i];
    -  }
    -  return out << ']';
    -}
    -
    -

    Incomplete types in templates