Шаблон вычета для функции на основе его типа возврата?

StackOverflow https://stackoverflow.com/questions/2612961

Вопрос

Я хотел бы иметь возможность использовать шаблон вычета для достижения следующего:

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

Это было полезно?

Решение

Это не может быть сделано. Тип возврата не принимает участие в вычете типа, это скорее результат уже соответствует соответствующему шаблону подписи. Тем не менее, вы можете скрыть его от большинства применений как:

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

Другие советы

Единственное, о чем я могу подумать: сделать выделить не шаблон, который возвращает прокси-объект без шаблона, который имеет оператор преобразования с шаблоном, который делает реальную работу:

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();
}

Вы можете пойти на противоположный маршрут.

Если вы используете актуальный компилятор (MSVC 2010, который должен быть через пару дней, или текущая версия GCC) и не против полагаться на функции C ++ 0x:

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

сэкономит вам дополнительный <A> а также <B>, просто не на правой стороне. :)

(Этот ответ такой же, как @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)...};
}

Кроме того, вышеупомянутые работы для функций Two Arg или Treg Arg, не сложно видеть, как расширить это.

Это много кода, чтобы написать! Для каждой функции вы будете применять это, вы можете написать макрос, который делает это для вас. Что-то вроде этого в верхней части вашего файла:

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

И тогда вы могли бы использовать auto_foo В вашей программе.

Точно так же вы не можете перегружать функции на возвращенном типе, вы не можете сделать вычет шаблона на нем. И по той же причине - если f () - это шаблон / перегрузка, которая возвращает что-то, какой тип использования здесь:

f();

Вы можете попробовать использовать макрос для него. Кроме того, я не вижу, как это должно работать с одним утверждением.

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

ALLOC(ptr1,A);

Точки Йоханнес действуют. Проблема >> легко исправлена. Но я думаю, что запятые в рамках типа требуются C99 Preprocessor Varargs расширение:

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

ALLOC(ptr1,SomeTemplate<int,short>);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top