TITLE: Ambiguity in declaration vs expression?

PROBLEM: jamshid@ses.com, 2 Mar 95

Shiping encountered some code like the following that was giving a
compiler error on the IBM AIX compiler (xlC).

	class TQueryData {
	    //...
	public:
	    TQueryData ();

	    class SQueryList {  // a nested class
		//...
	    public:
		SQueryList() {}
	    };
	    void setListValue( int , const TQueryData& );

	    TQueryData (const SQueryList&);
	};

	void mkToken (const TQueryData& type)
	{
	   TQueryData token (TQueryData::SQueryList());
	   token.setListValue(0, type);  // error: left side must be an object
	}   

Apparently the compiler didn't think "token" was declared or
initialized correctly.  It turns out that xlC interprets the
declaration of "token" as a function instead of a local variable
definition!  It's as if the code was:

	void mkToken (const TQueryData& type)
	{
	   TQueryData token ( TQueryData::SQueryList (*) () );
	   token.setListValue(0, type);  // error: left side must be an object
	}   


Ugh, an IBM compiler bug, right?  NO, xlC is correct!  Our code (and
Cfront, which we use on the Sun and HP) is incorrect.  In fact, I
think all but one problem Shiping has encountered porting the C++ Wb
3.0 branch to xlC have been due to bugs in our code.  Hopefully he can
move all these changes onto the trunk too so that we don't have to
re-fix our code the next time we port to another (correct) compiler.
Our guidelines should probably be updated to include problems found
when porting to xlC.  I hope to see more cross-compiler concurrent
development to nip code bugs like these before they spread.

Why xlC is interpreting the code like this is not obvious.  In C and
C++ you can declare function parameters without naming them and
without using the "(*)" syntax.  The following declarations of f() are
all equivalent.

	void f( int (*callback)() );  // legal, good style
	void f( int (*)() );          // legal, bad style (no parameter name)
	void f( int (callback)() );   // legal, bad style (function==func. ptr)
	void f( int callback() );     // legal, bad style (parens redundant)
	void f( int () );             // legal, bad style (no parameter name)

This should help you see why the above definition of "token" was
interpreted as a function prototype.  The ARM (6.8 Ambiguity
Resolution) rule is that "TQueryData::SQueryList()" is interpreted as
a declaration (ie, the function parameter type "TQueryData::SQueryList
(*)()") if at all possible instead of being interpreted as an
expression (ie, construct a temporary SQueryList object).

There's only a couple of workarounds I can think of:

1. Use the "=" for of initialization instead of parenthesis.  This
requires that the copy constructor be defined (many of our classes do
not implement them).  Note: the "=" below does NOT have anything to do
with the assignmnt operator function.  The "T a=b;" style of
initialization is often deprecated for efficiency reasons (technically
it requires a temporary objects to be created), but good compilers
will optimize it to be the same as the "T a(b);".

	void mkToken (const TQueryData& type)
	{
	   TQueryData token = TQueryData::SQueryList();

2. Use an explicit temporary.  This is kinda ugly and verbose.  Alternative:
   declare and use a global "const TQueryData::SQueryList gEmptyQueryList;".

	void mkToken (const TQueryData& type)
	{
	   TQueryData::SQueryList tmp;
	   TQueryData token(tmp);
