Usando auto e decltype em C ++ 11
Pergunta
Eu estou tentando aprender as características atualmente aceitos de c ++ 11 e eu estou tendo problemas com auto e decltype. Como um exercício de aprendizagem Eu estou estendendo a lista de classe std com algumas funções genéricas.
template<class _Ty, class _Ax = allocator<_Ty>>
class FList : public std::list<_Ty, _Ax>
{
public:
void iter(const function<void (_Ty)>& f)
{
for_each(begin(), end(), f);
}
auto map(const function<float (_Ty)>& f) -> FList<float>*
{
auto temp = new FList<float>();
for (auto i = begin(); i != end(); i++)
temp->push_back(f(*i));
return temp;
}
};
auto *ints = new FList<int>();
ints->push_back(2);
ints->iter([](int i) { cout << i; });
auto *floats = ints->map([](int i) { return (float)i; });
floats->iter([](float i) { cout << i; });
Para o mapa membro quero o tipo de retorno para ser genérico dependendo do que os retornos função passada. Assim, para o tipo de retorno que eu poderia fazer algo parecido com isso.
auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>*
Isso também seria necessário para remover o tipo float no modelo função.
auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*
Eu poderia usar uma classe de modelo, mas que faz com que o uso de instâncias mais detalhado desde que eu tenho para especificar o tipo de retorno.
template<class T> FList<T>* map(const function<T (_Ty)>& f)
Para resumir a sua pergunta eu estou tentando descobrir como definir mapa sem o uso de uma classe de modelo e ainda tê-lo genérico no tipo ele retorna.
Solução
Derivando de std::list
ou outros recipientes std::
é desencorajada.
Escreva suas operações como funções livres para que possam trabalhar em qualquer recipiente padrão através iterators.
Você quer dizer "definir mapa sem o uso de uma função de modelo"?
Você deve ser capaz de usar o tipo de membro result_type
de std::function
para obter o tipo ele retorna.
Além disso, não é necessário que você especificar que a função é passado como um std::function
. Você poderia deixá-la aberta como qualquer tipo, e deixar o compilador juntar tudo. Você só precisa std::function
para o polimorfismo em tempo de execução.
E usando nova para criar objetos pilha de alocação de cru e devolvê-los pelo ponteiro é soooo 1992! :)
A sua função iter é essencialmente a mesma coisa que a gama- base para o laço .
Mas tudo isso de lado ... você algo dizer assim?
template <class TFunc>
auto map(const TFunc &f) -> FList<decltype(f(_Ty()))>*
{
auto temp = new FList<decltype(f(_Ty()))>();
for (auto i = begin(); i != end(); i++)
temp->push_back(f(*i));
return temp;
}
Isso irá corresponder a qualquer coisa que pode ser chamado, e vai descobrir o tipo de retorno da função usando decltype.
Note que ele requer _Ty ser construível padrão. Você pode obter em torno de que pela fabricação de uma instância:
template <class T>
T make_instance();
Nenhuma implementação é necessária porque nenhum código é gerado que o chama, de modo que o vinculador não tem nada a queixar-se (graças a dribeas para apontar isto!)
Assim, o código torna-se agora:
FList<decltype(f(make_instance<_Ty>()))>*
Ou, literalmente, uma lista de qualquer que seja o tipo seria que você deseja obter a partir de chamar a função f com uma referência a uma instância de _Ty.
E como um bônus livre para aceitar, procurar referências rvalue - estes significa que você pode escrever:
std::list<C> make_list_somehow()
{
std::list<C> result;
// blah...
return result;
}
E, em seguida, chamá-lo assim:
std::list<C> l(make_list_somehow());
Porque std :: lista terá um "construtor de movimento" (como um construtor de cópia, mas escolhida quando o argumento é um temporário, como aqui), ele pode roubar o conteúdo do valor de retorno, ou seja, fazer o mesmo como um ideal swap
. Portanto, não há cópia de toda a lista. (É por isso C ++ 0x fará ingenuamente escrito run código existente mais rápido - muitos populares, mas feias truques de desempenho se tornará obsoleto).
E você pode obter o mesmo tipo de coisa de graça para qualquer classe existente de seu próprio país, sem ter que escrever um construtor movimento correto, usando unique_ptr
.
std::unique_ptr<MyThing> myThing(make_my_thing_somehow());
Outras dicas
Você não pode usar auto em argumentos da função em que você deseja que os tipos dos argumentos a ser deduzido. Você usa modelos para isso. Dê uma olhada em: http://thenewcpp.wordpress.com/2011/10/18/ a palavra-chave-auto / http://thenewcpp.wordpress.com/2011/10/25/ decltype-e-declval / . Ambos explicam como usar o auto e decltype. Eles devem lhe dar informações suficientes sobre como eles são usados. Em particular, uma outra resposta sobre make_instance poderia ser feito melhor com declval.
Eu acho que o ponto que Jarrah estava fazendo em sua resposta é que esses pontos não explicar exatamente como usar essas coisas. Vou apontar os detalhes de qualquer maneira:
Você não pode fazer isso, há duas coisas erradas:
auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*
auto
não pode ser usado para os argumentos da função. Se você quer o tipo de função a ser deduzido, então você deve usar modelos. A segunda é que decltype
toma uma expressão, enquanto que _Ty
é um tipo. Aqui é como resolvê-lo:
template <typename Ret>
auto
map(const function<Ret (_Ty)>& f)
-> FList<decltype(f(declval<_Ty>()))>
{}
Dessa forma, não há mágica para criar uma instância de um tipo.