Como retornar o tipo correto de dados nos modelos?


9
#include <iostream>
using namespace std;

template <class X, class Y>
Y big(X a, Y b)
{
   if (a > b)
      return (a);
   else return (b);
}

int main()
{
   cout << big(32.8, 9);
}

Aqui estou usando modelos no CPP; portanto, quando chamo a função bigignorando argumentos de doublee intdigite, quero a resposta de retorno que é double. O tipo aqui, ele retorna em 32vez de 32.8.

Como obtenho a saída desejada? Como escrever um tipo de retorno de bigfunção adequado ?


11
Uma função pode retornar apenas um tipo fixo. Você não pode escolher, em tempo de execução, que tipo retornar.
Jesper Juhl 15/01

11
Você pode querer ver como std::maxé implementado. O tipo de retorno de uma função deve ser conhecido em tempo de compilação em C ++. Portanto, você não pode ter esse tipo de retorno dependente do valor de tempo de execução dos seus parâmetros. É por isso que, para essa função, você precisa que os dois parâmetros tenham o mesmo tipo (ou seja, possuem o tipo X, mas não o Y).
Boris Dalstein

Respostas:


12

Uma função pode ter apenas um tipo de retorno que precisa ser conhecido no momento da compilação. No entanto, você pode usar std::common_type, para retornar um tipo no qual os dois parâmetros possam ser implicitamente convertidos.

Isso seria

#include <type_traits>
template <class X, class Y>
typename std::common_type<X,Y>::type big(X a, Y b)
{
   if (a > b)
      return a;
   else return b;
}

E para verificar se, na verdade, retorna a doublequando passou a inte a double, podemos fazer:

int main() {
    auto x = big(4.2,42);
    std::cout << std::is_same<decltype(x),double>::value;
}

Que imprime

1

PS: std::common_typepode usar o operador ternário por trás das notas e, como tal, esta solução não é muito diferente das outras respostas ( auto+ ternário). O poder real std::common_typeé que ele aceita qualquer número de parâmetros.


10

O tipo de retorno deve ser determinado em tempo de compilação. Você pode usar o retorno à direita com um operador condicional , se estiver limitado a .

template <typename X, typename Y>
auto big(X&& a, Y&& b) -> decltype(a > b ? a : b) // ---> like this
{
   return  a > b ? a : b;
}

Ver ao vivo


No entanto, se você tiver acesso ao ou superior, o autoretorno é suficiente, pois o compilador deduzirá o tipo certo se você o usar junto com o operador condicional da seguinte maneira:

template <typename X, typename Y>
auto big(X a, Y b)
{
   return  a > b ? a : b;
}

Ver ao vivo


O tipo de retorno à direita não é necessário, pelo menos a partir do C ++ 14.
sweenish 15/01

@walnut Bom ponto. mais uma opção é a referência de encaminhamento?
JeJo 16/01

11
@JeJo Sim, suponho que isso também esteja bem, mas provavelmente sem sentido, porque você não está modificando nenhum argumento e o tipo de retorno ainda será uma referência de valor l em ambos os casos (embora potencialmente não const).
walnut

Eu removi meus comentários porque eles não se aplicam mais, mas sugiro adicionar um aviso à resposta de que você não pode usar os parâmetros por valor.
walnut

Se alguém procurar no seu código, parece que o parâmetro passado decidirá qual tipo de retorno alguém receberá, o que não é o caso! Você sempre receberá um dobro, mesmo que a seja maior que b.
Klaus

4

Ao marcar seu tipo de retorno como Ye passar um intcomo seu segundo parâmetro, você indicou claramente que Yé um int. Não há surpresas acontecendo aqui.

#include <iostream>

template <typename X, typename Y>
decltype(auto) big(const X& a, const Y& b)  // return type can just be auto as well 
{
    return a > b ? a : b;
}

int main()
{
    std::cout << big(32.8, 9) << '\n';
    std::cout << big(9, 32.8) << '\n';
    std::cout << big(32.8, 90) << '\n';
    std::cout << big(90, 32.8) << '\n';
}

Isso imprime todos os quatro valores corretos na tela.

https://godbolt.org/z/fyGsmo

Uma coisa que é importante observar é que isso funcionará apenas para tipos que podem ser comparados entre si, ou seja, o compilador converterá implicitamente um tipo em outro para a comparação.

IMPORTANTE : Os parâmetros precisam ser tomados por referência para evitar comportamento indefinido. Isso tem a ver com o tipo de retorno pelo qual estou teimosamente. decltype(auto)pode retornar referências a tipos. Se você retornar algo local para a função (contagem de argumentos), obterá um comportamento indefinido.


O retorno acidental de uma referência é muito mais difícil do que este site parece ser. Mas é bom saber sobre o comportamento indefinido. Não é como se fosse um código que eu escreveria de qualquer maneira; é uma resposta para uma pergunta.
sweenish 16/01

11
Ah Eu li o seu comentário anterior como dois pontos distintos e não como efeito e causa. Eu posso fazer a edição apropriada.
sweenish 16/01

Adicionei um aviso adicional.
sweenish 16/01

2

Esta não é a solução correta para sua situação precisa, com toda a probabilidade - as outras respostas provavelmente estarão muito mais próximas do que você deseja.

No entanto, se você realmente precisar retornar tipos totalmente diferentes em tempo de execução por algum motivo, a solução correta (desde o ) é usar a std::variant, que é uma espécie de união com segurança de tipo.

#include <variant>

template <typename X, typename Y>
std::variant<X, Y> max(X a, Y b) {
  if (a > b)
    return std::variant<X, Y>(std::in_place_index_t<0>, a);
  else
    return std::variant<X, Y>(std::in_place_index_t<1>, b);
}

Observe que o ônus está no chamador para lidar com o valor retornado, provavelmente usando std::visitou algo parecido.


-2

Ele retorna int porque Y é um int e lança 32,8 para ele. Quando você chamou big 32,82, é float, mas 8 é int e o tipo de retorno da função é Y, que também é int.

Você não pode realmente consertar isso, pois precisa saber em tempo de execução que tipo retornos grandes, portanto, faça aeb do mesmo tipo:

    #include <iostream>
    using namespace std;

    template <typename X>

    X big (X a, X b)
    {
    if (a>b)
    return a;

    else return b;
    }

    int main()
    {
    cout<< big (32.8, 9.0);
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.