tags:

views:

484

answers:

2

i did this in msvc 2005.

typedef void (*cleanup_t)();

void func(cleanup_t clean)
{
    cleanup_t();
}

Why does this compile? and not give me a warning? ok, it gave me a unreferenced formal parameter warning but originally i did this when clean was in a class no there was no unreferenced formal parameter when this code gave me problems.

What is cleanup_t(); really doing and what is the point? now for laughs i tried int() and that worked also.

+11  A: 

I think it's an expression that evaluates to a default value for a cleanup_t type. In other words, an expression that returns a NULL pointer to a function that returns void.

In C/C++, expressions with no side effects (which this is - I think) are valid statements, just like you can have a statement like:

1 + 2;

That's not a syntax error, but some compilers might give a warning. They don't often give a warning for side-effect-less expressions that return NULL values or are simply variable names because that type of expression is often used in macros for debugging purposes (like the assert() macro).

You can think of it as calling the default constructor for the cleanup_t type. Having this default constructor-like syntax for built-in types (or typedef's of them) was added to C++ so that templates could set items of the type passed in as a template parameter to default values while still allowing the template type parameter to be a non-user defined type. There might other reasons, but I believe that to be one of them.

Something like:

template <class T>
class foo
{
    T myT;

    public:

    foo() {
        myT = T();
    };
};

typedef void (*cleanup_t)();


class bar
{
};


int not_quite_a_cleanup_t_func()
{
    return 1;
}


int main()
{
    foo<int> intFoo;
    foo<cleanup_t> cleanup_t_foo;
    foo<bar> barFoo;

    // here I'm going to harp on one of the things I don't like about C++:
    //
    //  That so many things that look like function calls are not or that
    //  the parens cause subtle behavior changes.
    //
    //  I believe this is the reason this question was posted to 
    //  stackoverflow, so it's not too far off topic.
    //  
    //  Many of these things exist because of backwards compatibility with C or
    //  because they wanted to fit in new features without adding keywords or
    //  new reserved tokens or making the parser even more complex than it already
    //  is.  So there are probably good rationales for them.
    //
    //  But I find it confusing more often than not, and the fact that there
    //  might be a rationale for it doesn't mean I have to like it...

    cleanup_t cleanup1();    // declares a function named cleanup1 that returns a cleanup_t

    cleanup_t cleanup2 = cleanup_t();   // cleanup2 is a variable of type cleanup_t that 
                                        //  is default initialized

    cleanup_t* cleanup3 = new cleanup_t;    // cleanup3 is a pointer to type cleanup_t that 
                                            //  is initialized to point to memory that is 
                                            //  *not* initialized

    cleanup_t* cleanup4 = new cleanup_t();  // cleanup4 is a pointer to type cleanup_t that
                                            //  is initialized to point to memory that *is*
                                            //  initialized (using default intialization)

    cleanup2 = cleanup_t( not_quite_a_cleanup_t_func);  // explicit type conversion using functional notation

    cleanup_t();    // the OP's problem
    cleanup2();     // call the function pointed to by cleanup2
    (*cleanup2)();  // same thing

    class cleanup_class
    {
        cleanup_t cleanup5;

    public:
        cleanup_class() : 
            cleanup5() // class member default initialization
        { };
    };
}
Michael Burr
+8  A: 

It's executing a default initializer for the cleanup_t type to create a temporary of that type, and then never actually using that temporary.

It's a lot like a constructor call, the "MyClass()" part of "MyClass c = MyClass();", except that pointer-to-function types don't actually have constructors. Of course in my code snippet here, "MyClass()" doesn't necessarily create a temporary, because it's an initializer expression. The "MyClass()" in "MyClass().some_method();" is perhaps a closer analogy.

"int()" is another way of saying "int(0)", which is another way of saying "(int)0", which is another way of saying "0". Again, it assigns to a temporary, and if that's the whole statement then the temporary is unused.

If you compile the code in the question with -Wall on GCC, you get a warning "statement has no effect". The code a person doing this might have meant to type, "clean();", wouldn't produce that warning because of course it would have the effect of calling the function. Yet another reason to switch warnings on, and fix 'em properly ;-)

Steve Jessop