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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top