Template deduction for function based on its return type?
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 t开发者_如何学JAVAhis:
class GC
{
public:
template <typename T>
static GCPtr<T> Allocate();
};
Would this be possible to knock off the extra <A>
and <B>
?
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>
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();
}
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. :)
(This answer is the same as @UncleBens, but a bit more general as it perfect-forward any arguments.)
This is very useful in languages like haskell where, for example, read
will take a string as input and will parse it according to the desired return type.
(Here is sample code on ideone.)
First, start with the function foo
whose return type we wish to deduce:
template<typename Ret>
Ret foo(const char *,int);
template<>
std::string foo<std::string>(const char *s,int) { return s; }
template<>
int foo<int >(const char *,int i) { return i; }
When asked for a string, it'll return the string that's in its first argument. When asked for an int, it'll return the second argument.
We can define a function auto_foo
that can be used as follows:
int main() {
std::string s = auto_foo("hi",5); std::cout << s << std::endl;
int i = auto_foo("hi",5); std::cout << i << std::endl;
}
To make this work, we need an object that will temporarily store the function arguments, and also run the function when it is asked to convert to the desired return type:
#include<tuple>
template<size_t num_args, typename ...T>
class Foo;
template<typename ...T>
class Foo<2,T...> : public std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this)); }
};
template<typename ...T>
class Foo<3,T...> : std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this), std::get<2>(*this)); }
};
template<typename ...T>
auto
auto_foo(T&&... args)
// -> Foo<T&&...> // old, incorrect, code
-> Foo< sizeof...(T), T&&...> // to count the arguments
{
return {std::forward<T>(args)...};
}
Also, the above works for two-arg or three-arg functions, it's not difficult to see how to extend that.
This is a lot of code to write! For each function you will to apply this to, you could write a macro that does this for you. Something like this at the top of your file:
REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
// necessary structure and auto_???
and then you could use auto_foo
in your program.
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();
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>);
精彩评论