views:

671

answers:

4

Hi,

how do I the second parameter become optional?

template <typename T>
inline void Delete (T *&MemoryToFree,
 T *&MemoryToFree2 = ){
 delete MemoryToFree;
 MemoryToFree = NULL;

 delete MemoryToFree2;
 MemoryToFree2 = NULL;

}

I tried several things after the = operator, like NULL, (T*)NULL etc can this be done?

the only way the compiler let me do it was this: using overload...

    template <typename T, typename T2>
inline void Delete (T *&MemoryToFree, T2 *&MemoryToFree2){
 delete MemoryToFree;
 MemoryToFree = NULL;

 delete MemoryToFree2;
 MemoryToFree2 = NULL;
}

just to learn...

+10  A: 

You could just overload the function

template <typename T>
inline void Delete (T *&MemoryToFree){
        delete MemoryToFree;
        MemoryToFree = NULL;
}

template <typename T, typename T2>
inline void Delete (T *&MemoryToFree, T2 *&MemoryToFree2){
        delete MemoryToFree;
        MemoryToFree = NULL;

        delete MemoryToFree2;
        MemoryToFree2 = NULL;
}
Bruce
Yes that is the only other option.
Simon H.
yeah, thats what I did, I just wanted to know if tehre was an option and if not... why
Jonathan
+2  A: 

No this is not possible. You cannot use default paramters with references.

Simon H.
+1: You could mention that's only for references that are not references-to-const.
Troubadour
True, but I've had two glasses of wine so I'm not thinking that clearly.
Simon H.
reference to const? you mean that I could use T MemoryToFree const = NULL ?
Jonathan
@Jonathan: See my answer for details.
Troubadour
GMan
Can you provide a default value that is a static `T`? If so, the function could then check to see if the arg is that special case.
Loadmaster
It's possible also for non-const references. But it's just not worth it. You can do `template<typename T> struct d { T t,
Johannes Schaub - litb
Notice the importance of initializing `t` (done using `t()`), otherwise we will delete some random pointer value.
Johannes Schaub - litb
You can use defualt arguments with *absolutely any* reference parameters, regardless of whether they are const or not. The only issue here is the generation of the proper defuault value.
AndreyT
@litb: In that example you seem to jump through lots of hoops in order to cheat the lvalue/rvalue rules of C++, while the same thing can be achieved by a simple template function (as in my reply)?
AndreyT
@AndreyT, not with the same semantics: In my example, each time we call, we get a freshly new default argument. In your code, you will always have the old argument of before. Neither your nor my code is necessarily better, but i feel having it always fresh better fits the semantics of a default argument. But as a non-const reference does not have a *value* aspect, but an *identity* aspect, we may well care about *what* object is the default argument, and so your way has benefits too since you can test whether or not the default argument is used.
Johannes Schaub - litb
@litb: Agreed, in this case the default argument (initialized to zero) is processed "naturally", since 'delete null-pointer' is a no-op. In other cases the ability to "recognize" the default argument inside the body of the function might come handy.
AndreyT
So even if technically possible, why go through all that? In my opinion the overloaded function method is a lot cleaner.
Simon H.
@Simon H: I would mildly disagree in general :) "Overloaded function" approach sometimes (or often) leads to logic and/or code duplication, which is never good. In this particular case it is easy to avoid, by calling one overloaded function from another (the author of the accepted solution should've done that, BTW), but in general it can be a PITA.
AndreyT
BTW in my comment above there should only be one `typename T` - no `typename T2` (`T2` couldn't be deduced when only one argument is provided). This is another downside of the default-arg way: You cannot have the type of the param that has a default argument be a template parameter different from all other parameter types anymore.
Johannes Schaub - litb
A: 

The following code complies fine for me using gcc 4.3.2.

void fn( int*const & i = 0 )
{
    delete i;
}

so maybe you could adapt that. You need the *const to permit the default argument. Default arguments are permitted for references-to-const.

Edit

Just realised that marking it *const is no use to you since you want to be able to null off the pointer as well as delete it.

Troubadour
hi, thanks for the post... it didn't compile on the line Memory2 = NULL. template <typename T> inline void Delete (T * MemoryToFree = NULL; delete MemoryToFree2; MemoryToFree2 = NULL; }
Jonathan
wow, it messed up hte code blady, what I meant is that the definition was ok, but the implementation when nullifying the pointer it said I couldn't
Jonathan
Interesting. I've actually pasted your original code into a file and tried it _without_ the *const and gcc compiles and runs it fine if I call Delete< int >( a, b ). Yet if I replace T with int manually and re-compile the code it complains and requires the *const. Weird. I think I might start on the wine like Simon H ;)
Troubadour
I suppose with the template you don't instantiate the version that uses a default parameters. Templated code does not tend to produce errors, if not used. (One would have to dig deep into the standard to be 100% sure this behavior is correct.) However, all in all, the usefulness of the two-argument version seems somewhat dubious in the first place...
UncleBens
@UncleBens: That's it, thanks. When I actually let it fall back to using the default parameter it choked.
Troubadour
+3  A: 

You could always write a simple "on-demand static lvalue generator" and use it as the default value for your parameter

template <typename T> inline T& get_lvalue() {
  static T t;
  return t;
}

In your code

template <typename T> 
inline void Delete(T *&MemoryToFree, T *&MemoryToFree2 = get_lvalue<T*>())
AndreyT
+1 for another solution useful sometimes :)
Johannes Schaub - litb
+1... very nice!
Jonathan
One thing to keep in mind with this method is that all functions will share the same default value. So if you have two completely unrelated functions, they will share the same default value, even though each one will think they own the default value exclusively. This doesn't only apply to one TU, of course, but the value is shared among all TUs. So for `Delete` the pre-condition has to be the value of the pointer is `0x0`, but the post-condition of a `Hex2Ptr` can be that the pointer is written some non-null value. If the call sequence is `Hex2Ptr, Delete`, then mad things will happen.
Johannes Schaub - litb
One way out of course would be to pass a "tag" to `get_lvalue`, which could look like `get_lvalue<T*, struct Delete>()`, and which will give it its own default arg.
Johannes Schaub - litb
Another way would be to in fact always check in the body of `Delete` whether the default argument was passed and if a default arg is passed, don't delete anything. This kinda defeats the whole purpose of a default argument somewhat, though (because i think ideally behavior should not depend on whether the user explicitly passes an argument or whether a default argument was used - the default argument should be the possibly best choice).
Johannes Schaub - litb