33

我希望能够使用模板推导来实现以下目标:

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

而不是(我目前拥有的):

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

我当前的分配函数如下所示:

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

这有可能取消额外的<A><B>吗?

4

6 回答 6

34

那是做不到的。返回类型不参与类型推导,而是已经匹配了适当的模板签名的结果。但是,您可以将其从大多数用途中隐藏起来:

// 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>()是另一个问题。

PS c++11 将允许您跳过其中一种类型声明:

auto p = GC::Allocate<A>();   // p is of type GCPtr<A>
于 2010-04-10T10:14:51.857 回答
32

我唯一能想到的:使 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();
}
于 2010-04-10T10:22:23.550 回答
8

你可以走相反的路线。

如果您使用的是最新的编译器(MSVC 2010 应该会在几天后发布,或者当前版本的 GCC)并且不介意依赖 C++0x 功能:

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

会为您节省额外的<A>and <B>,而不是在右侧。:)

于 2010-04-10T11:09:28.530 回答
4

(这个答案与@UncleBens 相同,但更通用一点,因为它可以完美转发任何参数。)

这在像 haskell 这样的语言中非常有用,例如,read将字符串作为输入并根据所需的返回类型对其进行解析。

(这是ideone 上的示例代码。)

首先,从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;
}

为了完成这项工作,我们需要一个对象来临时存储函数参数,并在被要求转换为所需的返回类型时运行该函数:

#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)...};
}

此外,以上适用于两个参数或三个参数的函数,不难看出如何扩展它。

这要写很多代码!对于您将应用它的每个函数,您可以编写一个宏来为您执行此操作。文件顶部是这样的:

REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
                        // necessary structure and auto_???

然后你可以auto_foo在你的程序中使用。

于 2013-11-08T17:52:38.517 回答
1

以同样的方式,您不能在返回类型上重载函数,也不能对其进行模板推导。出于同样的原因 - 如果 f() 是返回某些内容的模板/重载,那么在这里使用什么类型:

f();
于 2010-04-10T10:19:27.293 回答
0

您可以尝试使用宏。除此之外,我不明白这应该如何只使用一个语句。

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

ALLOC(ptr1,A);

约翰内斯的观点是有效的。>> 问题很容易解决。但我认为将逗号作为类型的一部分需要 C99 预处理器可变参数扩展:

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

ALLOC(ptr1,SomeTemplate<int,short>);
于 2010-04-10T10:15:33.507 回答