SourceForge.net Logo

[Release b]
<t4m_bug_copy_ctor>
Wed Jun 23 16:39:23 WEST 2004

In the current release of tendra4minix (a), after fixing <t4m_bug_usage>,
the program copy_ctor.C produces this error,

"copy_ctor.C", line 37: Error:
  [ISO 14]: The non-exported template 'TreeIterator < int >::TreeIterator ( const TreeIterator < int > & )' has not been defined.

It's asking for the copy constructor, which has been declared but not defined.
Although this is true, the fact is that it shouldn't need the copy constructor
for compiling this program. This is a subtle bug, because if we give a
definition for the copy constructor uncommenting the '{ assert(0); }' block,

  TreeIterator(const TreeIterator<T> &) { assert(0); }

then the program does get compiled and executed without the copy constructor
being called. But there are more puzzling things. If we call member function
'template <class T> Tree<T>::flush()' inside 'main' uncommenting the
corresponding line,

  Tree<> t;
  //TreeIterator<> i = t;
  t.flush();

then the program gets compiled, but the copy constructor for TreeIterator<int>
is called inside 'flush',

assertion failed: 0, file copy_ctor.C, line 23

The problem disappears when we use the function-call operator for initializing
the iterator object (leaving the copy constructor undefined),

  TreeIterator(const TreeIterator<T> &);

...

  TreeIterator<T> i(*this);

...

  TreeIterator<> i(t);
  t.flush();

(it stops asking for the copy constructor). Of course, as usual, the problem
disappears when Tree and TreeIterator are not template classes.

Well, this is not a bug. The spec X3J16/96-0225 WG21/N1043, on section
8.5 Initializers [dcl.init], says,

11The  form  of  initialization  (using  parentheses  or =) is generally
  insignificant, but does matter when the entity being initialized has a
  class  type;  see below.  A parenthesized initializer can be a list of
  expressions only when the entity being initialized has a class type.

12The initialization that occurs in argument passing,  function  return,
  throwing   an   exception   (_except.throw_),  handling  an  exception
  (_except.handle_),    and     brace-enclosed     initializer     lists
  (_dcl.init.aggr_)  is  called copy-initialization and is equivalent to
  the form
          T x = a;
  The  initialization  that  occurs  in  new  expressions  (_expr.new_),
  static_cast expressions (_expr.static.cast_), functional notation type
  conversions  (_expr.type.conv_),  and  base  and  member  initializers
  (_class.base.init_)  is called direct-initialization and is equivalent
  to the form
          T x(a);

Later on, paragraph 14, it says that, for class types, the semantics of
copy-initialization is different from that of direct-initialization, and that
the former is done through the creation of a temporary object whose type is the
same as the type of the object being initialized. The temporary is then
direct-initialized, and the object being initialized is initialized from the
temporary object using the copy constructor. The spec adds that "In certain
cases, an implementation is permitted to eliminate the temporary by
initializing the object directly;" (this explains why the copy constructor was
not called inside 'main'), but [class.temporary] "Even when the creation of the
temporary object is avoided, all the semantic restrictions must be respected as
if the temporary object was created." This part explains the error message
above; see also the comment at function remove_temporary [initialise.c].