The Casting Vote

by Sean A. Corfield

The standards process has now reached a very interesting stage. In my last column I said we would know the result of the Committee Draft Registration (CDR) ballot and whether we would be progressing to the ballot that produces a Draft International Standard. The result of the CDR ballot was as follows: 8 countries voted to register the draft with no comments, 5 countries voted "yes with comments" and 2 countries voted "no". This means that we will register the CD and proceed into the next ballot bringing an International Standard much closer. According to the current schedule, the Draft International Standard will be produced at the end of 1995.

What of the comments, though? France and the Netherlands voted "no" because the standard is too large and complex and the library is too large and not yet stable. Both Australia and New Zealand made similar comments but voted in favour. So is the draft really too big? Judge for yourself - we are shortly to enter the ANSI public review: send a mail message to

c++std-notify@research.att.com

and when the public review starts you will be notified with details of how to make comments to X3J16 (the U.S. C++ committee). You can also make comments on the draft through your national standards body and for those of you in the UK, you can send your comments to

c++comments@maths.warwick.ac.uk

and a member of the UK C++ panel will collate them so that the panel can review them and feed them into the standards process. How will you get access to the Committee Draft? The c++std-notify list will tell you - the draft will be available by anonymous ftp in PostScript and probably PDF formats from both U.S. and UK sites. The review period is not very long so it is imperative you get your comments in as soon as possible.

In the meantime, committee business was conducted pretty much as usual in Austin.

Exception safety

Various concerns about exception handling were addressed in Austin. One concern was memory leaks when placement new throws an exception:

char buffer[SIZE];
X* p = new (buffer) X;
// if X::X() throws an exception,
// no cleanup is done

For the simple example above, the lack of cleanup is not a problem - the placement new used does not allocate memory. If placement new is, say, a pool allocator, then any memory allocated will not be deleted if the constructor throws an exception. The solution to this was proposed by Bill Gibbons and adopted in Austin: define a placement delete that is called automatically in such circumstances. For each operator new you define, you will now be able to define a matching operator delete that will be called by the implementation if an exception is thrown by the constructor:

void* operator new(size_t, bool);
void operator delete(void*, bool);
X* p = new (true) X;
// if an exception is thrown by X's
// constructor, operator delete will be
// called as:
operator delete(p, true);

Another exception-related issue is constructor initialisers - with the existing language definition, there is no way to catch an exception thrown by a member initialiser or base class initialiser. This is a problem if you delegate work to a library object and don't want your users to see the exceptions thrown by that library:

class X
{
public:
	X() : libObj() { }
private:
	LibObj	libObj;
};

If the LibObj constructor throws an exception, it will propagate to users of class X. This lack of encapsulation led to a proposal to add a try block that encloses the initialisers:

X::X() throw (XException)
try
:	libObj()
{
}
catch (LibException)
{
	throw XException();
}

For symmetry, this syntax is also allowed on ordinary functions.

As I note elsewhere in this issue, the fact that new throws an exception on failure is a problem in itself. In Austin, the committee decided that the previous "implementation-defined" hack of allowing set_new_handler to restore the old behaviour - returning zero on failure - was precisely that: a hack. The solution adopted was originally proposed by John Skaller, I believe, and is extremely elegant: simply provide a placement new form that guarantees no exceptions will be thrown. The exact details still need some work, but essentially you will be able to write something like:

X* p = new (nothrow) X;

and guarantee that if new fails, it returns zero instead of throwing an exception. This almost allows you to compile your code with:

-Dnew='new (nothrow)'

and keep your old behaviour. Almost? Well, you'll have to #include the appropriate header to get the definition of nothrow (no, I don't know where it will end up) and it will break any existing placement new's you may have. The committee did not add a symmetric placement delete but this may yet appear.

More template extensions

At the San Diego meeting in March 1994 the committee added member templates to the language and some library proposals adopted since have relied on this new feature. Quite a few implementors have expressed concern that member templates are very hard, or even impossible, to implement. In many cases, an alternative to member templates would be the ability to partially specialise a template, e.g., given a generic list class, it would be desirable to be able to specialise this for all pointer types. This facility has now been added for both functions and classes:

template<class T> void f(T);  // #1
template<class T> void f(T*); // #2
int i;
int* p;
f(i);	// calls #1 void f<int>(int)
f(p);	// calls #2 void f<int>(int*)

This is a combination of specialisation and overloading. If #1 and #2 had been declared in separate translation units, this probably worked on many implementations. For template classes the situation is somewhat more complicated:

template<class T> class X { ... };
template<class U> class X<U*> { ... };

The second declaration specialises the first for all pointer types. It doesn't declare a template, despite its appearance, but instead specifies a more specialised form of the first template declaration. The partial specialisation can appear to have more template arguments:

template<class T, class C> class X<T C::*>
{ .... };

which specialises X for all pointers to members. Similarly, specialisations can appear to have fewer template arguments:

template<class A, class B> class Y { ... };
template<class P> class Y<P, P*> { ... };

The second declaration is a specialisation of the first - the template Y still has two arguments. I don't know about you, but I thought this was sufficiently confusing to vote against the proposal. Unfortunately, the majority of the committee did not share my concerns and adopted partial specialisation.

Returning briefly to member templates, the committee resolved a syntactic problem with explicit qualification of member function template calls. There are circumstances where syntax analysis cannot know whether an identifier is a template or not:

template<class T>
class Strange
{
...
	int odd()
	{
		return T::f<1>(2);
	}
...
};

Since we don't know what f is (other than it being a member of T), we don't know whether this is an explicit qualification of a template member function of T or a double comparison:

class Static
{
public:
	static int f;
};
class Member
{
public:
	template<int n> void f(int);
};
Strange<Static> ss; // (Static::f < 1 ) > 2
Strange<Member> sm; // Member::f<1> ( 2 )

In a similar decision to the use of typename to identify member types in dependent names, the committee decided to allow the use of template to identify a member template:

template<class T>
class NotSoStrange
{
...
	int odd()
	{
		return T::template f<1>(2);
	}
...
};

Here, the identifier f is always treated as a template name and the <1> means explicit qualification (as in Strange<Member> above). Without the keyword template, the example would mean a double comparison of a static member (as in Strange<Static> above).

And now, for something
completely...

...awful. Namespaces provide a way to parcel up components in a way that avoids name pollution but they have introduced many problems for name lookup. Consider the following program:

#include <iostream.h>
int main()
{
	cout << "Hello world!\n";
}

At the moment this works because operator<< is either a member function (of ostream) or a global function. With namespaces, this would become:

#include <iostream>
int main()
{
	std::cout << "Hello world!\n";
}

This only works, under the current rules, if operator<< is a member function for the char* operand (it is). What if the operand is of a different type?

#include <iostream>
#include <complex>
int main()
{
	std::complex<double> unity(1, 0);
	std::cout << unity << '\n';
}

Perhaps surprisingly, this will not work because the necessary operator<< is in the namespace std and will not be found by name lookup because that scope is not searched under the current rules. The proposed solution was to additionally look in the namespace of the types of the operands, which would solve the above problem (by searching std), but I noted that this would not be sufficient:

#include <iostream>
#include <complex>
namespace MyLib
{
	class MyComplex
	: public std::complex<double>
	{
	// ...
	};
}
int main()
{
	MyLib::MyComplex unity(1, 0);
	unity + unity;
}

Here, operator+ is defined in std and the operand types are both defined in MyLib. The "obvious" answer was to extend the name lookup to also search base class namespaces. This leads to the following possibilities for finding an operator:

  1. a member function (found by existing rules),
  2. a global operator (found by existing rules),
  3. a built-in operator (found by existing rules),
  4. an operator declared in the namespace of the types of the operands (new rule),
  5. an operator declared in the namespace of any base class of the types of the operands (new rule).

This is the change I alluded to in my comment at the end of George Wendle's article. In the above example:

	unity + unity;

the search proceeds as follows:

  1. look for a member function of MyLib::MyComplex (there isn't one)
  2. look for a global operator+ (may find some but assume we don't)
  3. look for a built-in operator+ (find several dealing with built-in types)
  4. look in the namespaces of the operands (MyLib has no operator+ declarations)
  5. look in the namespaces of the base classes of the operands (std certainly has a suitable operator+)

These are all thrown in the pot for overload resolution where, we hope, std::complex operator+(std::complex, std::complex) wins!

As I said - awful. The UK did not support this but it does seem to solve the problem. Hopefully, someone can come up with examples that have undesirable behaviour under these rules and we can revisit the issue.

Out, out, implicit int!

At a previous meeting the committee voted to ban implicit int in a couple of places and deprecate it everywhere else. This meant that a future standard may consider removing the feature. However, the C standard is undergoing revision and it seems likely that WG14 (the ISO C committee) will deprecate, or possibly ban, implicit int. In this light, the C++ committee revisited their decision and decided to ban implicit int everywhere in the language.

Name injection

Although no vote was taken on this subject, it is a bubbling cauldron for many members of the committee. Here is an example that highlights the problem:

class A
{
	friend void f();	// injected
				// immediately
};
template<class T> class B
{
	friend void g();	// injected
		// only at instantiation
};
void h()
{
	f();		// fine - injected by
			// declaration of A
	g();		// error - no such
			// name in scope
	B<int>	b;	// causes injection of
			// friend g()
	g();		// fine - name
			// injected by
			// instantiation of B
}

This may not seem too outrageous but consider this example:

template<class T> class C
{
	friend void injected();
};
template<class U> void t(U)
{
	C<U>	c;
}
void innocent()
{
	injected();	// error - no such
			// name in scope
	t(1);		// call t<int>(1) and
			// instantiate C<int>
			// which injects
			// injected()
	injected();	// fine - name has
			// been injected
}

Somehow this seems more insidious than the previous example as no explicit template class has been used that could inject the name. What about the following?

void confused()
{
	t(1), injected();	// valid?
}

It becomes important exactly when an instantiation occurs. The Extensions WG were particularly uncomfortable with this example as it leads to the idea of instantiation sequence points (i.e., madness).

The German delegation are very concerned about this and, in my opinion, rightly so. The potential for confusion is high. In Austin, I suggested that instantiation be done in a synthesised scope so that injected names could not affect the original scope. In the example above, that would mean that the injection of injected() due to the use of C<int> would occur only local to t<int>(1) and would not affect the innocent() function. This seemed to gain support amongst the Extensions WG and we agreed to investigate this further. I hope that we can cap the problems of name injection at the next meeting.

Future meetings

The next ISO/ANSI meeting takes places in July �95 so The Casting Vote will next appear in the August issue - Overload 9. In the meantime, the public review will have begun so I hope to have many articles from you, the public, about the Committee Draft.

An aside

Because the general public feeling is that we (the committee) should not be adding to language, in Austin we took the decision to disband the Extensions WG - its members will now turn their attention to core language issues such as the "One Definition Rule"...and the many extensions that have been voted into the core language over the last few years.

Sean A. Corfield