views:

137

answers:

5

I'd like to be able to use template deduction to achieve the following:

GCPtr<A> ptr1 = GC::Allocate();
GCPtr<B> ptr2 = GC::Allocate();

instead of (what I currently have):

GCPtr<A> ptr1 = GC::Allocate<A>();
GCPtr<B> ptr2 = GC::Allocate<B>();

My current Allocate function looks like this:

class GC
{
public:
    template <typename T>
    static GCPtr<T> Allocate();
};

Would this be possible to knock off the extra < A> and < B>?

Thanks

+11  A: 

That cannot be done. The return type does not take part in type deduction, it is rather a result of having already matched the appropriate template signature. You can, nevertheless, hide it from most uses as:

// helper
template <typename T>
void Allocate( GCPtr<T>& p ) {
   p = GC::Allocate<T>();
}

int main()
{
   GCPtr<A> p = 0;
   Allocate(p);
}

Whether that syntax is actually any better or worse than the initial GCPtr<A> p = GC::Allocate<A>() is another question.

P.S. c++11 will allow you to skip one of the type declarations:

auto p = GC::Allocate<A>();   // p is of type GCPtr<A>
David Rodríguez - dribeas
A: 

You could try to use a macro for it. Other than that, I don't see how that's supposed to work with just one statement.

#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>()

ALLOC(ptr1,A);

Johannes' points are valid. The >> issue is easily fixed. But I think having commas as part of the type requires the C99 preprocessor varargs extension:

#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()

ALLOC(ptr1,SomeTemplate<int,short>);
sellibitze
Notice that this macro fails if you do `ALLOC(ptr1, A<a, b>);` (there are two problems: No space after `type` (aka' `>>`) and the comma makes two macro arguments out of `A<a, b>`).
Johannes Schaub - litb
And what would that buy you? You'd still have to mention the type, and it's less safe than David's solution with an inlined function template. -1 from me.
sbi
You can solve both problems by saying `ALLOC(ptr1, (A<a, b>));` and rewriting the macro to pass a function-type to `template<typename T> struct ty; template<typename Ty> struct ty<void(Ty)> { typedef Ty type; };` and say `GCPtr<ty<void type>::type> ptrname` instead (and the same with `typename` for use within templates. C++0x and some current c++03 compilers allows `typename` also outside of templates, though).
Johannes Schaub - litb
@sbi: Sure, I wouldn't use such a macro in my code. That that was the only thing that came to my mind. And of course you have to name the type at least once even with Davids solution.
sellibitze
@ltb: that's mighty clever to work around C99 varargs macros. But it does have the problem that you need two versions if the type depends on template arguments.
sellibitze
+1  A: 

In the same way you can't overload functions on return type, you can't do template deduction on it. And for the same reason - if f() is a template/overload that returns something, what type to use here:

f();
anon
Well I've thought about that already. My garbage collector class uses reference counting, and calling GC::Allocate() will inherently have 0 references which would just get cleaned up anyway. This is of course if the code compiled/
Marlon
Compiler error, unless appearing in a cast (`(int)f();`) ...?
UncleBens
@UncleBens: nice idea! However, the C++ compiler doesn't currently work this way.
Vlad
@Marlon I think you have commented on the wrong answer.
anon
@UncleBens Say what????
anon
@Neil, what I was trying to say was I have already thought about what happens when f() is called by itself (a compile error). Now replace f() with GC::Allocate() and imagine it did compile. My garbage collector uses reference counting and since the return value is not stored in a GCPtr the reference count is at 0 and the garbage collector would just clean it up instantly. This is all hypothetical since the code doesnt actually compile.
Marlon
@Neil: I mean, this is how overloading and type deduction based on return type could *hypothetically* work if it existed.
UncleBens
@UncleBens Oh well, hypothetically... I don't think that would be sufficient reason to reintroduce C-style casts though. And it would make overloaded functions work differently from non-overloaded ones, WRT the return type.
anon
@Neil: You could use a `static_cast` too. I don't think the mechanism would be too different from disambiguating between overloads by casting arguments (or selecting which conversion operator to invoke). Inconvenient to use it happily everywhere, but nice to have if you need it.
UncleBens
@UncleBens But what if you do actually want to cast the return value?
anon
@Neil: Good point. `int foo(); long foo(); static_cast<double>(foo());` -> ambiguous overloads. Workaround: `long l = foo(); static_cast<double>(l);`, or `static_cast<double>(static_cast<long>(foo());`
UncleBens
@Neil: Then you would have to do two nested casts. Not nice, but there's cases we have to do that now. Note that Ada has had overloading based on return type for decades. (It's not like I'm desperately missing it in C++, it's just that it, in principle, is a possible feature.)
sbi
+5  A: 

The only thing I can think of: make Allocate a non-template that returns a non-template proxy object that has a templated conversion operator which does the real work:

template <class T>
struct GCPtr
{

};

class Allocator
{
public:
    template <class T>
    operator GCPtr<T>() { return GCPtr<T>(); }
};

class GC
{
public:
    static Allocator Allocate() { return Allocator(); }//could give a call-back pointer?
};

int main()
{
    GCPtr<int> p = GC::Allocate();
}
UncleBens
It seems overkill, but still, I did not know this pattern. You taught me something. So +1.
paercebal
Anyway, at first glance, I guess you could avoid the GC::Allocate() altogether and write : `GCPtr<int> p = Allocator() ;` , no ?
paercebal
As the comment says, the Allocator object could store additional data that it receives through the constructor, so GC::Allocate can decide what data it needs for the operation. - Eventually the constructor of `GCPtr<T>` could do the work itself (invoke `GC::Allocate<T>`).
UncleBens
A: 

You could go the opposite route.

If you're using an up to date compiler (MSVC 2010 which should be out in a couple of days, or the current version of GCC) and don't mind relying on C++0x features:

auto ptr1 = GC::Allocate<A>();
auto ptr2 = GC::Allocate<B>();

would save you the extra <A> and <B>, just not on the right hand side. :)

jalf