代码之家  ›  专栏  ›  技术社区  ›  Marlon

基于函数返回类型的模板推导?

  •  26
  • Marlon  · 技术社区  · 15 年前

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

    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> ?

    6 回复  |  直到 6 年前
        1
  •  29
  •   David Rodríguez - dribeas    15 年前

    那是不可能的。返回类型不参与类型推导,而是由于已经匹配了适当的模板签名。不过,您可以将其隐藏起来,以防大多数情况下使用:

    // 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>
    
        2
  •  23
  •   UncleBens    15 年前

    我唯一能想到的是: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();
    }
    
        3
  •  8
  •   Stack Overflow is garbage    15 年前

    你可以走相反的路线。

    如果你正在使用一个最新的编译器(MSVC 2010,它应该在几天之内,或者GCC的当前版本),并且不介意依赖C++ +0x特性:

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

    会为你省下额外的钱 <A> <B> 不在右手边。:)

        4
  •  4
  •   Aaron McDaid    11 年前

    (这个答案与@unclebens相同,但更一般一些,因为它完善了任何论点。)

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

    (这里是 sample code on 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;
    }
    

    为了使这项工作正常进行,我们需要一个临时存储函数参数的对象,并在需要时运行函数。 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_???
    

    然后你可以用 自动传真机 在你的程序中。

        5
  •  1
  •   anon    15 年前

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

    f();
    
        6
  •  0
  •   sellibitze    15 年前

    您可以尝试对其使用宏。除此之外,我不知道这是如何工作,只有一个声明。

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

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

    #define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()
    
    ALLOC(ptr1,SomeTemplate<int,short>);