我希望能够使用模板推导来实现以下目标:
GCPtr<A> ptr1 = GC::Allocate();
GCPtr<B> ptr2 = GC::Allocate();
而不是(我现在拥有的):
GCPtr<A> ptr1 = GC::Allocate<A>();
GCPtr<B> ptr2 = GC::Allocate<B>();
我当前的Allocate功能如下所示:
class GC
{
public:
template <typename T>
static GCPtr<T> Allocate();
};
是否可以取消额外的<A>
和<B>
?
答案 0 :(得分:29)
无法做到。返回类型不参与类型推导,而是已经匹配了适当的模板签名的结果。但是,您可以将其隐藏在大多数用途中:
// helper
template <typename T>
void Allocate( GCPtr<T>& p ) {
p = GC::Allocate<T>();
}
int main()
{
GCPtr<A> p = 0;
Allocate(p);
}
这种语法实际上是否比初始GCPtr<A> p = GC::Allocate<A>()
更好或更差是另一个问题。
P.S。 c ++ 11将允许您跳过其中一个类型声明:
auto p = GC::Allocate<A>(); // p is of type GCPtr<A>
答案 1 :(得分:25)
我唯一能想到的是:make Allocate一个非模板,它返回一个非模板代理对象,它有一个模板化的转换操作符,可以完成真正的工作:
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();
}
答案 2 :(得分:8)
你可以走相反的路线。
如果您使用的是最新的编译器(MSVC 2010应该在几天之内,或者当前版本的GCC)并且不介意依赖于C ++ 0x功能:
auto ptr1 = GC::Allocate<A>();
auto ptr2 = GC::Allocate<B>();
会为您节省额外的<A>
和<B>
,而不是在右侧。 :)
答案 3 :(得分:4)
(这个答案和@UncleBens一样,但是因为它完善了任何参数,所以会更加通用。)
这在像haskell这样的语言中非常有用,例如,read
将字符串作为输入,并根据所需的返回类型对其进行解析。
首先,从我们希望推断出其返回类型的函数foo
开始:
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; }
当被要求输入字符串时,它将返回第一个参数中的字符串。当被要求输入int时,它将返回第二个参数。
我们可以定义一个函数auto_foo
,可以按如下方式使用:
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;
}
为了使这项工作,我们需要一个临时存储函数参数的对象,并在要求convert提供所需的返回类型时运行该函数:
#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)...};
}
此外,上述适用于两个arg或三个arg函数,不难看出如何扩展它。
这是很多代码要编写的!对于要应用此功能的每个函数,您可以编写一个为您执行此操作的宏。文件顶部有类似的内容:
REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
// necessary structure and auto_???
然后您可以在程序中使用auto_foo
。
答案 4 :(得分:1)
同样地,你不能在返回类型上重载函数,你不能对它进行模板推导。出于同样的原因 - 如果f()是一个返回内容的模板/重载,那么在这里使用什么类型:
f();
答案 5 :(得分:0)
您可以尝试使用宏。除此之外,我不认为这应该只用一个陈述。
#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>()
ALLOC(ptr1,A);
约翰内斯的观点是有效的。 &gt;&gt;问题很容易解决。但我认为将逗号作为该类型的一部分需要C99预处理器varargs扩展:
#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()
ALLOC(ptr1,SomeTemplate<int,short>);