Topic: The "variable definition vs. function declaration" problem
Author: Daryle Walker <thedl0-usenet1@yahoo.com>
Date: Mon, 3 Jan 2005 11:31:14 CST Raw View
[I was going to publish this here and on c.l.c++, but decided to put it
just in c.l.c++. Someone there suggested that I publish it here though.]
This is about the common confusion over:
CONFUSION := type-expression identifier "(" (PART ("," PART)*)? ")"
PART := type-expression "(" identifier ")"
Within a function's (or member function's) definition, such constructs
are meant to be a variable definition with constructor call 99.99%[A] of
the time. However, compilers must always interpret it as a function
declaration, turning it into a no-op. Why must the compiler (almost)
always do the wrong thing here? Can we change the rules? The issues,
besides the lack of a real solution, include:
{1} Allowing function forward declarations within a function was a
carry-over from C. I say that this is unnecessary to keep any longer.
It has now become dangerous with the introduction of name spaces.
namespace daryle
{
int f()
{
double g( char ); // C doesn't have this scenario
return 5;
}
}
void h()
{
void k();
}
Quick, is "g" really "::daryle::g" or "::g"? Or does it use some form
of lookup?[B] Is the answer different for "k"? This feature is the
only one I know of where lexical containment does NOT mean compile-item
containment. (Dropping this misfeature could clear a way for actual
nested functions.) Does the current interpretation have any legitimate
use?
{2} The same parsing confusion happens outside of function definitions,
where it pits function prototypes against global objects. Here, we
actually need function declarations. We need some way of forcing the
"global object" case.
{3} As someone in the comp.lang.c++ version of this thread said,
compiler makers _still_ do not flag this problem (within function
definitions). Can any of them here give an explanation for that? There
would have to be two warning switches, one for any function declaration
within a function definition (default on), and another for function
declarations outside function definitions that seem to have excess
parentheses (default off). Sorry that the latter is fairly vague, but
the former should be very definitive to determine.
[A] 42.9% of statistics are made up
[B] What is the answer to this, anyway?
--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: richard@ex-parrot.com
Date: Wed, 5 Jan 2005 09:48:33 CST Raw View
Daryle Walker wrote:
> [I was going to publish this here and on c.l.c++, but decided to put
it
> just in c.l.c++. Someone there suggested that I publish it here
though.]
>
> This is about the common confusion over:
>
> CONFUSION := type-expression identifier "(" (PART ("," PART)*)?
")"
>
> PART := type-expression "(" identifier ")"
>
> Within a function's (or member function's) definition, such
constructs
> are meant to be a variable definition with constructor call 99.99%[A]
of
> the time. However, compilers must always interpret it as a function
> declaration, turning it into a no-op. Why must the compiler (almost)
> always do the wrong thing here? Can we change the rules?
This breaks backwards compatibility, both with C and with current C++.
Just because most of the time, most people don't put function
declarations at block-scope, doesn't mean that no-one ever does.
Changing the rules *will* break existing code.
> namespace daryle
> {
> int f()
> {
> double g( char ); // C doesn't have this scenario
>
> return 5;
> }
> }
>
> void h()
> {
> void k();
> }
>
> Quick, is "g" really "::daryle::g" or "::g"?
g is ::daryle::g -- a block-scope function declaration declares the
function in the inner-most enclosing namespace scope.
> Is the answer different for "k"?
k is again declared at the inner-most enclosing namespace scope. This
time it is in the global namespace.
> This feature is the
> only one I know of where lexical containment does NOT mean
compile-item
> containment.
Friend declarations?
> (Dropping this misfeature could clear a way for actual
> nested functions.)
It's arguable how useful it would be to be able to forward declare
nested (i.e. block-scope) functions. The current grammar would extend
to block-scope function *definitions* without difficulty. If you
allowed forward declarations of block-scope functions (breaking
compatibility with the existing language), would you allow them to
defined "out-of-line"?
class Class {
void outer() const;
};
void Class::outer() const {
int inner();
}
int void Class::outer() const::inner() {
return 42;
}
Finding a convenient syntax for this definition is likely to be
problematic.
> Does the current interpretation have any legitimate
> use?
Yes. Resolving ambiguities in overload resolution:
// Two ambiguous overloads, perhaps from different headers in
// different parts of the program. Obviously good progamming
// techniques would minimise the likelyhood of this occuring.
void f( int = 0 );
void f( long = 0 );
int main() {
void f(int = 0);
f(); // f(int);
}
Or allowing code (perhaps templated in a header) to access an
implementation-detail without injecting the name into the applicable
namespace.
namespace library {
template <class T> void my_algorithm() {
void helper(); // don't make visible elsewhere
helper();
}
}
// In .cc file
namespace library {
void helper();
}
> {2} The same parsing confusion happens outside of function
definitions,
> where it pits function prototypes against global objects. Here, we
> actually need function declarations. We need some way of forcing the
> "global object" case.
Extra parentheses can usually be used to resolve the ambiguity in
favour of an object declaration.
int f1( int() );
int f2(( int() ));
In this case f1 is a function return an int and taking one argument, a
pointer to a function returning an int and taking no arguments.
However, f2 is int initialised from a default-constructed int. I.e. f1
is a function, f2 is an object.
> {3} As someone in the comp.lang.c++ version of this thread said,
> compiler makers _still_ do not flag this problem (within function
> definitions).
Very often they do, simply for the reason that it is unusual to declare
an object and not then use it. So:
int main() {
int i(); // I meant to default-construct an integer
i = 42; // Error: i is a function
}
Of course, there are occasions when you do not get an error:
mutex my_mutex;
void f() {
scoped_lock s( mutex ); // Ooops forgot the 'my_'.
// code not using s.
}
I find the best way to avoid this is to program defensively -- whenever
I declare a scoped_lock object (as I meant to here), I would write
auto scoped_lock s( my_mutex );
That wat, if I accidentally omit the 'my_' prefix and so declare a
function, I will get an error about it being illegal for a function to
be declared 'auto'. Although I wouldn't usually advocate using the
auto keyword, this is one situation where I would usually try to use
it. (Especially given that the runtime errors resulting from the
missing locks might easily be overlooked, even if the library has a
good test suite.)
> Can any of them here give an explanation for that?
Lack of demand?
> There
> would have to be two warning switches, one for any function
declaration
> within a function definition (default on), and another for function
> declarations outside function definitions that seem to have excess
> parentheses (default off).
The latter is probably not what you want. Take this example
int f( int() );
-- it declares a function, f, returning int and taking one argument, a
pointer to a function returning int and taking no arguments. A clearer
way of declaring this (in my opinion) would be
int f( int (*)(void) );
This has more paretheses -- not fewer. Perhaps you are thinking about
older compilers (e.g. some versions of gcc before 3.4) where
int f(( int() ));
does not correctly disambiguate the declaration to be that of an
object. Surely, though, the correct solution here is to fix the
compiler? (As, indeed, was done.)
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]