From 489722fb5c2f8b6642c1665b33d8751b752cc83c Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 17 Mar 2010 07:10:56 +0000 Subject: [PATCH] Add another compatibility note and tweak a few of the existing ones. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98717 91177308-0d34-0410-b5e6-96231b3b80d8 --- www/cxx_compatibility.html | 98 ++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/www/cxx_compatibility.html b/www/cxx_compatibility.html index 9bf9865687..e5fd63ef9c 100644 --- a/www/cxx_compatibility.html +++ b/www/cxx_compatibility.html @@ -23,7 +23,8 @@
  • Introduction
  • Variable-length arrays
  • Initialization of non-integral static const data members within a class definition
  • -
  • Dependent name lookup into dependent bases of class templates
  • +
  • Unqualified lookup in templates
  • +
  • Unqualified lookup into dependent bases of class templates
  • Default initialization of const variable of a class type requires user-defined default constructor
  • @@ -97,87 +98,110 @@ class SomeClass { const double SomeClass::SomeConstant = 0.5; +Note that the forthcoming C++0x standard will allow this. + -

    Dependent name lookup into dependent bases of class templates

    +

    Unqualified lookup in templates

    Some versions of GCC accept the following invalid code:
    -template <typename T>
    -class Base {
    - public:
    -  void DoThis(T x) {}
    +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
    +
    + +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. +

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

    Unqualified lookup into dependent bases of class templates

    + + +Some versions of GCC accept the following invalid code: + +
    +template <typename T> struct Base {
    +  void DoThis(T x) {}
       static void DoThat(T x) {}
     };
     
    -template <typename T>
    -class Derived : public Base<T> {
    - public:
    +template <typename T> struct Derived : public Base<T> {
       void Work(T x) {
         DoThis(x);  // Invalid!
         DoThat(x);  // Invalid!
       }
     };
    -
    -void Test() {
    -  Derived<int> d;
    -  d.Work(42);
    -}
     
    -Clang correctly rejects it with the following errors: +Clang correctly rejects it with the following errors +(when Derived is eventually instantiated):
    -my_file.cpp:13:5: error: use of undeclared identifier 'DoThis'
    +my_file.cpp:8:5: error: use of undeclared identifier 'DoThis'
         DoThis(x);
         ^
         this->
    -my_file.cpp:20:5: note: in instantiation of member function 'Derived<int>::Work' requested here
    -  d.Work(42);
    -    ^
    -my_file.cpp:4:8: note: must qualify identifier to find this declaration in dependent base class
    +my_file.cpp:2:8: note: must qualify identifier to find this declaration in dependent base class
       void DoThis(T x) {}
            ^
    -my_file.cpp:14:5: error: use of undeclared identifier 'DoThat'
    +my_file.cpp:9:5: error: use of undeclared identifier 'DoThat'
         DoThat(x);
         ^
         this->
    -my_file.cpp:6:15: note: must qualify identifier to find this declaration in dependent base class
    +my_file.cpp:3:15: note: must qualify identifier to find this declaration in dependent base class
       static void DoThat(T x) {}
     
    -The reason the code is invalid is that in -class Derived<T>, the base class type Base<T> -depends on the template argument T (hence it's called a dependent base -class in C++ jargon), and C++ doesn't look at the members of a -dependent base class when resolving unqualified calls like DoThis(x) -and DoThat(x) (see [temp.dep] p3 for details). The fix, as Clang tells -you, is to prefix the calls with this->: +Like we said above, unqualified names like +DoThis and DoThat are looked up when the template +Derived is defined, not when it's instantiated. When we look +up a name used in a class, we usually look into the base classes. +However, we can't look into the base class Base<T> +because its type depends on the template argument T, so the +standard says we should just ignore it. See [temp.dep]p3 for details. + +

    The fix, as Clang tells you, is to tell the compiler that we want a +class member by prefixing the calls with this->:

    -...
    -template <typename T>
    -class Derived : public Base<T> {
    - public:
       void Work(T x) {
         this->DoThis(x);
         this->DoThat(x);
       }
    -};
    -...
     
    -Alternatively, since DoThat() is a static method, you can also write +Alternatively, you can tell the compiler exactly where to look:
       void Work(T x) {
    -    this->DoThis(x);
    +    Base<T>::DoThis(x);
         Base<T>::DoThat(x);
       }
     
    +This works whether the methods are static or not, but be careful: +if DoThis is virtual, calling it this way will bypass virtual +dispatch! +

    Default initialization of const variable of a class type requires user-defined default constructor

    -- 2.40.0