Dedução de modelo para função com base no seu tipo de retorno?
-
25-09-2019 - |
Pergunta
Eu gostaria de poder usar dedução de modelo para alcançar o seguinte:
GCPtr<A> ptr1 = GC::Allocate();
GCPtr<B> ptr2 = GC::Allocate();
em vez de (o que eu tenho atualmente):
GCPtr<A> ptr1 = GC::Allocate<A>();
GCPtr<B> ptr2 = GC::Allocate<B>();
Minha função de alocação atual se parece com a seguinte:
class GC
{
public:
template <typename T>
static GCPtr<T> Allocate();
};
Seria possível nocautear o extra <A>
e <B>
?
Solução
Isso não pode ser feito. O tipo de retorno não participa da dedução do tipo, é o resultado de já ter correspondido à assinatura do modelo apropriada. Você pode, no entanto, escondê -lo da maioria dos usos como:
// helper
template <typename T>
void Allocate( GCPtr<T>& p ) {
p = GC::Allocate<T>();
}
int main()
{
GCPtr<A> p = 0;
Allocate(p);
}
Se essa sintaxe é realmente melhor ou pior do que a inicial GCPtr<A> p = GC::Allocate<A>()
é outra pergunta.
O PS C ++ 11 permitirá que você pule uma das declarações de tipo:
auto p = GC::Allocate<A>(); // p is of type GCPtr<A>
Outras dicas
A única coisa em que consigo pensar: faça alocar um não-tempo que retorne um objeto proxy que não seja do tempo que possui um operador de conversão modelo que faz o trabalho real:
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();
}
Você pode seguir o caminho oposto.
Se você estiver usando um compilador up up Det (MSVC 2010, que deve ser lançado em alguns dias ou a versão atual do GCC) e não se importa de confiar nos recursos C ++ 0x:
auto ptr1 = GC::Allocate<A>();
auto ptr2 = GC::Allocate<B>();
economizaria para você o extra <A>
e <B>
, apenas não do lado direito. :)
(Esta resposta é a mesma que @unclebens, mas um pouco mais geral, pois é perfeita para os argumentos.)
Isso é muito útil em idiomas como Haskell, onde, por exemplo, read
Pegará uma string como entrada e a analisará de acordo com o tipo de retorno desejado.
(Aqui está Código de amostra em ideone.)
Primeiro, comece com a função foo
cujo tipo de retorno desejamos deduzir:
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; }
Quando solicitado uma string, ela retornará a string que está em seu primeiro argumento. Quando solicitado um INT, ele retornará o segundo argumento.
Podemos definir uma função auto_foo
que pode ser usado da seguinte maneira:
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;
}
Para fazer isso funcionar, precisamos de um objeto que armazenará temporariamente os argumentos da função e também executará a função quando for solicitado converter para o tipo de retorno desejado:
#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)...};
}
Além disso, o acima funciona para funções de dois ou três áreas, não é difícil ver como estendê-lo.
Isso é muito código para escrever! Para cada função para a qual você aplicará isso, você pode escrever uma macro que faça isso por você. Algo assim no topo do seu arquivo:
REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
// necessary structure and auto_???
E então você pode usar auto_foo
em seu programa.
Da mesma maneira que você não pode sobrecarregar as funções no tipo de retorno, você não pode fazer dedução do modelo. E pelo mesmo motivo - se f () é um modelo/sobrecarga que retorna algo, que tipo de usar aqui:
f();
Você pode tentar usar uma macro para isso. Fora isso, não vejo como isso deve funcionar com apenas uma declaração.
#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>()
ALLOC(ptr1,A);
Os pontos de Johannes são válidos. O problema >> é facilmente corrigido. Mas acho que ter vírgulas como parte do tipo exige a extensão do pré -processador C99:
#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()
ALLOC(ptr1,SomeTemplate<int,short>);