Bagaimana cara mengembalikan tipe data yang benar dalam templat?

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);
}

Di sini saya menggunakan template di CPP, jadi ketika saya memanggil fungsi bigmem-bypass argumen doubledan intmengetik, saya ingin jawaban balik yang double. Ketik di sini, ia mengembalikan 32bukan 32.8.

Bagaimana saya mendapatkan hasil yang saya inginkan? Bagaimana cara menulis jenis bigfungsi pengembalian yang tepat ?

Rakshanda Meshram
sumber
1
Suatu fungsi hanya dapat mengembalikan satu tipe tetap. Anda tidak dapat memilih pada waktu berjalan jenis apa yang akan dikembalikan.
Jesper Juhl
1
Anda mungkin ingin melihat bagaimana std::maxpenerapannya. Tipe pengembalian fungsi harus diketahui pada waktu kompilasi dalam C ++. Jadi Anda tidak dapat memiliki tipe pengembalian ini bergantung pada nilai runtime dari parameter Anda. Inilah sebabnya mengapa untuk fungsi tersebut, Anda memerlukan kedua parameter untuk memiliki tipe yang sama (yaitu, memiliki tipe X, tetapi tidak Y).
Boris Dalstein

Jawaban:

12

Suatu fungsi hanya dapat memiliki satu tipe pengembalian yang harus diketahui pada waktu kompilasi. Namun, Anda dapat menggunakan std::common_type, untuk mengembalikan tipe yang kedua parameter dapat secara implisit dikonversi.

Itu akan menjadi

#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;
}

Dan untuk memeriksa apakah itu benar-benar mengembalikan doubleketika dilewati intdan doublekita dapat melakukan:

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

Yang dicetak

1

PS: std::common_typedapat menggunakan operator ternary di belakang layar dan dengan demikian solusi ini tidak jauh berbeda dari jawaban lain ( auto+ ternary). Kekuatan sebenarnya std::common_typeadalah ia menerima sejumlah parameter.

idclev 463035818
sumber
10

Jenis pengembalian harus ditentukan pada waktu kompilasi. Anda dapat menggunakan trailing return dengan operator bersyarat , jika Anda terbatas pada .

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

Lihat langsung


Namun, jika Anda memiliki akses ke atau autoreturn yang lebih tinggi sudah cukup, karena kompiler akan menyimpulkan tipe yang tepat jika Anda menggunakannya bersama dengan operator kondisional sebagai berikut:

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

Lihat langsung

JeJo
sumber
Tipe Trailing return tidak diperlukan, setidaknya pada C ++ 14.
sweenish
@walnut Poin bagus. satu opsi lagi adalah referensi penerusan?
JeJo
1
@ JoJo Ya, saya kira itu baik-baik saja, tapi mungkin tidak ada gunanya, karena Anda tidak mengubah argumen dan tipe kembali masih akan menjadi referensi nilai dalam kedua kasus (meskipun berpotensi non- const).
kenari
Saya menghapus komentar saya karena tidak berlaku lagi, tetapi saya sarankan menambahkan peringatan untuk jawaban bahwa Anda tidak dapat mengambil parameter berdasarkan nilai.
walnut
Jika seseorang melihat kode Anda, tampaknya parameter yang dikirimkan akan menentukan jenis pengembalian yang akan didapatkan seseorang yang bukan kasusnya! Anda akan selalu mendapatkan dua kali lipat, bahkan jika a lebih besar dari b.
Klaus
4

Dalam menandai jenis pengembalian Anda sebagai Ydan meneruskan intsebagai parameter kedua Anda, Anda telah dengan jelas menunjukkan bahwa itu Yadalah int. Tidak ada kejutan di sini.

#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';
}

Ini mencetak keempat nilai yang benar ke layar.

https://godbolt.org/z/fyGsmo

Satu hal yang penting untuk dicatat adalah bahwa ini hanya akan berfungsi untuk jenis yang dapat dibandingkan satu sama lain, yaitu, kompiler secara implisit akan mengkonversi satu jenis ke yang lain untuk perbandingan.

PENTING : Parameter perlu diambil dengan referensi untuk menghindari perilaku yang tidak terdefinisi. Ini ada hubungannya dengan tipe kembalinya aku dengan keras kepala menempel. decltype(auto)dapat mengembalikan referensi ke tipe. Jika Anda mengembalikan sesuatu yang lokal ke fungsi (jumlah argumen), Anda mendapatkan perilaku yang tidak terdefinisi.

sweenish
sumber
@walnut Secara tidak sengaja mengembalikan referensi jauh lebih sulit daripada yang dibuat situs ini. Tapi senang mengetahui tentang perilaku yang tidak terdefinisi. Bukannya ini kode yang akan saya tulis; itu adalah jawaban untuk sebuah pertanyaan.
sweenish
1
Ah. Saya membaca komentar Anda sebelumnya sebagai dua poin berbeda dan tidak berpengaruh dan menyebabkan. Saya dapat melakukan pengeditan yang sesuai.
sweenish
Saya telah menambahkan disclaimer tambahan.
sweenish
2

Ini bukan solusi yang tepat untuk situasi Anda yang tepat, dalam semua kemungkinan - jawaban lain cenderung lebih dekat dengan apa yang Anda inginkan.

Namun, jika Anda benar - benar perlu mengembalikan tipe yang sama sekali berbeda pada saat runtime karena suatu alasan, solusi yang benar (karena ) adalah dengan menggunakan a std::variant, yang merupakan jenis penyatuan tipe-aman.

#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);
}

Perhatikan bahwa kemudian tanggung jawab ada pada penelepon untuk menangani nilai yang dikembalikan, kemungkinan besar menggunakan std::visitatau sejenisnya.

N. Shead
sumber
-2

Ia mengembalikan int karena Y adalah sebuah int dan ia melemparkan 32,8 ke dalamnya. Ketika Anda memanggil big 32,82 adalah float, tetapi 8 adalah int dan tipe pengembalian fungsi adalah Y, yang juga int.

Anda tidak dapat benar-benar memperbaiki ini karena Anda perlu tahu pada saat runtime jenis pengembalian besar, jadi buatlah dan b jenis yang sama seperti ini:

    #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);
    }
Danuz991
sumber