[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].