Шаблон вычитания для функции на основе ее типа возврата?
Я хотел бы иметь возможность использовать шаблон вывода для достижения следующего:
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>
?
6 ответов
Это не может быть сделано. Возвращаемый тип не участвует в выводе типа, это скорее результат того, что он уже соответствует соответствующей сигнатуре шаблона. Тем не менее, вы можете скрыть это от большинства применений как:
// 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>
Единственное, о чем я могу думать: сделать 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();
}
Вы могли бы пойти противоположным путем.
Если вы используете новейший компилятор (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)...};
}
Кроме того, вышесказанное работает для функций с двумя или тремя аргументами, нетрудно понять, как их расширить.
Это много кода, чтобы написать! Для каждой функции, к которой вы хотите применить это, вы можете написать макрос, который сделает это за вас. Примерно так в верхней части вашего файла:
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);
Очки Йоханнеса действительны. Проблема >> легко решается. Но я думаю, что для использования запятых в качестве части типа требуется расширение varargs препроцессора C99:
#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()
ALLOC(ptr1,SomeTemplate<int,short>);