ISO/ IEC JTC1/SC22/WG21 N0726


Accredited Standards Committee X3       Doc No: X3J16/95-0126   WG21/N0726
Information Processing Systems          Date:    Jun 30, 1995
Operating under the procedures of       Project: Programming Language C++
American National Standards Institute   Ref Doc:
                                        Reply to: Josee Lajoie
                                                  (josee@vnet.ibm.com)
 

Issues on Special Member Functions and Proposed Resolutions
===========================================================
 
 *Issue Box 43:
  -------------
  12[special] paragraph 1 says:
    "[Note: ...  Often such special member functions are called
     implicitly.  The processor will implicitly declare these member
     functions for a class type when the programmer does not explicitly
     declare them."
 
  This text should indicate that implicitly-declared special member
  functions can only be called implicitly by the implementation.  User
  code cannot refer to or define these implicitly-declared special
  member functions.
 
  Proposal Box 43:
  ----------------
 
  I would like to make the following sentence part of the normative text:
    "The implementation shall implicitly declare the special member
     functions for a class type if the programmer does not explicitly
     declare them."
  and add the following text:
     "An implicitly-declared member function shall not be used
      explicitly or defined explicitly in a program.  For example,
 
        struct B {
          // B() implicitly declared
        };
        B::B() { } // ill-formed
     "
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue 22:
  ---------
  Are access restrictions only limitations on what the programmer may
  write?
  Must implementations also respect them when implicitly calling the
  special member functions?
  And if implementations must respect access restriction, when must it
  report the errors?
 
    struct B {
    private:
        ~B () { }
    };
 
    struct D : public B {
       ~D () { }  // is the mere existence of D::~D considered an
                  // implicit call of B::~B and therefore a protection
                  // violation?
    };
 
    void f() {
       D d;       // Or is this an error?
     }            // Or is the error when d is destroyed?
 
 
  Proposal 22:
  ------------
  Access restrictions must be respected for implicitly uses of the
  special member functions.
  Add text to 11 to indicate this.
  Paragraph 4 of 11.2 should say
    the rules apply to implicit use of special member function as well.
 
  Add to 12.1 after paragraph 5:
    "A program is ill-formed if the class for which a default
     constructor is explicitly defined has:
     -- a nonstatic data member of class type (or array thereof) with
        an inaccessible default constructor, or
     -- a base class with an inaccessible default constructor."
 
  Add to 12.4 after paragraph 3:
    "A program is ill-formed if the class for which a destructor is
     explicitly defined has:
     -- a non-static data member of class type (or array thereof) with
        an inaccessible destructor, or
     -- a base class with an inaccessible destructor."
 
  Add to 12.8 after paragraph 6:
    "A program is ill-formed if the class for which a copy constructor
     is explicitly defined has:
     -- a nonstatic data member of class type (or array thereof) with
        an inaccessible or ambiguous copy constructor, or
     -- a base class with an inaccessible or ambiguous copy constructor."
 
  Add to 12.8 after paragraph 11:
    "A program is ill-formed if the class for which a copy assignment
     operator is explicitly defined has:
     -- a nonstatic data member of class type (or array thereof) with
        an inaccessible copy assignment operator, or
     -- a base class with an inaccessible copy assignment operator."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue 379:
  ----------
  How can base class constructors and destructors be used by the
  derived class if they are not inherited?
 
  12.1[class.ctor] paragraph 3 says:
    "Constructors are not inherited."
 
  12.4[class.dtor] paragraph 6
    "Destructors are not inherited."
 
  12.8[class.copy] paragraph 5
    "Copy constructors are not inherited."
 
  May a constructor or destructor of a given base class type be invoked
  for an object whose type is the derived class type if the invocation
  is done using the class-qualified name syntax?  If, not, is an
  implementation obliged to issue a compile-time diagnostic for such
  usage?
 
    struct B {
             virtual ~B () { }
     };
 
     struct D : public B {
             ~D () { }
     };
 
     D D_object;
     D D_object2;
     B *B_ptr = &D_object2;
 
     void caller ()
     {
             D_object.B::~B();               // ok?
             B_ptr->~B();                    // ok?
     }
 
  Discussion 379:
  ---------------
  Regarding the fact that constructor and destructor are not inherited
  the ARM says (page 263):
    "Consider what might happen if constructors were inherited.
       class B {
       public:
         int a;
         B();
         B(int);
         B(B&);
       };
       class D : public B {
       public:
         int b;
       };
       f() {
         B b;
         D d1; // only B part initialized
         D d2 = b; // only B part of D2 initialized
         D d3 = d1; // only B part of D3 initialized
                    // this is not a copy of derived objects
         d3 = 1; // assignment to B part of D
       }
     ... Here, inheriting the constructor from the base class would
     quietly change the meaning of copying. The ability to assign
     an integer to a D would often be surprising."
 
  We already solve part of this problem when we decided that:
  o constructors were not functions but rather a particular syntax
    using the class name followed by '()'.
  o constructor and destructor is declared (either implicitly declared
    or explicitly declared) in every class.
 
  Since a constructor and destructor is declared (either implicitly
  declared or explicitly declared) for every class, it is always the
  class constructor or destructor that is called when an object of its
  class type is created/destroyed.  And I believe that the restrictions
  in the draft that indicate that constructors and destructors are not
  necessary anymore.
 
  Also, because constructors are not member functions, but a special
  syntax using the class name, constructors are never inherited.  I
  believe we should define equivalent semantics for destructors.
  (i.e. ~name is a special syntax using the class name anf it causes
  the destructor to be invoked...)
 
  Therefore, the syntax:
    D_object.B::~B();
  or
    D_object.~B();
  becomes valid if the name of the base class B is accessible in the
  class of D_object, and it causes the destructor to be invoked since
  the syntax for destructor calls is used.
 
  Proposal 379:
  -------------
  [change 12.4 to indicate that destructors are not member functions]
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue Box 44:
  -------------
  When is a default constructor implicitly-defined?
 
  12.1[class.ctor] paragraph 6 says:
    "An implicitly-declared default constructor for a class is
     implicitly defined when it is used to create an object of its class
     type."
 
  Proposal 44:
  ------------
  Add a footnote to say:
    "Default constructor are called implicitly to create objects of
     automatic storage duration or objects of static storage duration
     defined without an initializer (8.5) or are called when the
     explicit type conversion syntax (5.2.3) is used."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue 508:
  ----------
  When is a destructor implicitly-defined?
 
  12.4[class.dtor] paragraph 4 says:
    "An implicitly-declared destructor is implicitly defined when it is
     used to destroy an object of its class type."
 
  Proposal 508:
  -------------
  Add a footnote to say:
    "Destructors are called implicitly not only when an object of
     automatic storage duration goes out of scope, or at the end of a
     program for objects with static storage duration, but also in
     several situations due to the handling of exceptions."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue Box 50:
  -------------
  When is a copy constructor implicitly defined?
 
  Proposal Box 50:
  ----------------
  Add a footnote to 12.8[class.copy] paragraph 7 to say:
    "A copy constructor is used to initialize an object of its class
     type from a copy of an object of its class type or of a class
     derived from its class type.  See 8.5 for more details on direct
     and copy initialization."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue Box 47/48:
  -----------------
  Is a constructor with a parameter of type volatile X& or const
  volatile X& a copy constructor?
  Must an implicitly-declared copy constructor for class X be able to
  copy volatile objects?
  [Similar questions for copy assignment operators]
 
  See also paper 95-0056/N0656.
 
  The draft now indicates in footnote 76:
    "This implies that the reference parameter of the implicitly-declared
     copy constructor cannot bind to a volatile lvalue; see C.2.8."
 
  I do not remember the committee actually voting on this in Austin.
 
  Proposal Box 47/48:
  -------------------
  Leave things as is ;-)
  But describe this implications of this more clearly in 12.8
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue Box 55:
  -------------
  12.8[class.copy] paragraph 15:
    "Whenever a class object is copied and the implementation
     can prove that either the original or the copy will never again be
     used, an implementation is permitted to treat the original and the
     copy as two different ways of referring to the same object..."
 
  The text "can prove that either the original or the copy will never
  again be used" needs to be clarified.
 
  Proposal Box 55:
  ----------------
  The sentence above should be rewritten as:
    "Whenever a class object is copied into an object of the same type,
     and the implementation can prove that either the original or the
     copy will never again be used (except for the implicit call of the
     destructor at the end of the block or at the end of the program),
     an implementation is permitted to treat the original and the copy
     as two different ways of referring to the same object..."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue S1:
  ---------
  In Message c++std-core-5700, Erwin Unruh writes:
    "A return statement in a destructor does not directly return to
     the caller.  Instead it transfers control to the end of the body
     of the destructor.  Then the destructors of members and bases are
     called.  This is the behaviour most (all) of us would expect, but
     it is not described in the WP."
 
  Proposal S1:
  ------------
  Add what Erwin says at the end of paragraph 5.
    "A return statement in a destructor does not directly return to
     the caller.  Instead it transfers control to the end of the body
     of the destructor.  Then the destructors of members and bases are
     called."