std :: pair <auto, auto> ketik kembali

16

Aku sedang bermain-main dengan autodi std::pair. Dalam kode di bawah ini, fungsi fseharusnya mengembalikan std::pairtipe yang bergantung pada parameter templat.

Contoh kerja:

CONTOH 1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}

Ini bekerja dengan gcc 9.2, gcc 10.0, dentang 9.0 dan dentang 10.0.

Selanjutnya, saya ingin secara eksplisit menulis jenis pengembalian sebagai std::pairalasan kejelasan:

CONTOH 2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

Baik gcc 9.2 / 10.0 dan clang 9.0 / 10.0 gagal untuk mengkompilasi ini.

gcc 9.2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return

Dari pesan kesalahan terakhir, gcc 9.2 tampaknya percaya itu std::pair<auto, auto>adalah int. Bagaimana ini bisa dijelaskan?

gcc 10.0

error: returning initializer list

Kesalahan ini dapat dimengerti, namun, saya berharap konstruktor std::pairakan dipanggil, atau ada sesuatu yang saya lewatkan di sini?

dentang 9.0 dan 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

Ok, dentang tidak suka semua ini. Dari pesan kesalahan kedua, sepertinya dentang juga percaya tipe kembalinya int.

Akhirnya, untuk memperbaiki kesalahan yang diperoleh kompilasi dengan gcc 10.0, saya memutuskan untuk mengembalikan secara std::paireksplisit:

CONTOH 3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

dentang 9.0 dan 10.0

Sama seperti sebelumnya, tetapi dengan tambahan:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

Di sini dentang masih berpikir kita akan mengembalikan int?

gcc 9.2

Sama seperti sebelumnya.

gcc 10.0

Berhasil!

Saya kira beberapa fitur masih harus diimplementasikan, atau dalam salah satu situasi yang dijelaskan di atas, apakah ada kompiler yang benar dan yang lainnya salah? Menurut pendapat saya, contoh 2 harus bekerja. Atau seharusnya tidak?

mfnx
sumber

Jawaban:

23

Sintaksnya:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

Merupakan bagian dari Konsep TS asli tetapi tidak termasuk dalam proposal Konsep yang merupakan bagian dari C ++ 20. Dengan demikian, satu-satunya jenis placeholder dalam C ++ 20 adalah auto(dan variasi-variasi seperti itu auto**) decltype(auto),, dan placeholder terbatas ( Concept autodan variasi-variasi darinya). Jenis placeholder bersarang semacam ini akan sangat berguna, tetapi bukan bagian dari C ++ 20, sehingga deklarasi fungsi tidak terbentuk.

Sekarang, gcc memungkinkannya karena gcc mengimplementasikan Konsep TS dan saya kira mereka memutuskan untuk mempertahankan fitur ini. dentang tidak pernah menerapkan TS, jadi tidak.

Either way, ini:

std::pair<auto, auto> f() { return {1, 2}; }

Akan selalu buruk bentuknya. Arti sintaksnya adalah bahwa kita menyimpulkan tipe kembali dan kemudian mengharuskan itu cocok pair<T, U>untuk beberapa jenis Tdan U. Kami pada dasarnya mencoba menjalankan fungsi yang ditemukan:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

Tetapi Anda tidak dapat menyimpulkan suatu tipe dari {1, 2}- daftar yang di-brace-init tidak memiliki tipe. Mungkin ini adalah sesuatu yang harus dieksplorasi (karena mudah dimengerti setidaknya dalam kasus sederhana seperti ini), tetapi tidak pernah diizinkan. Jadi toh tolak itu benar.

Akhirnya:

gcc 9.2 tampaknya percaya bahwa itu std::pair<auto, auto>adalah int. Bagaimana ini bisa dijelaskan?

Untuk beberapa alasan (mungkin karena warisan C kami dengan implisit int), ketika gcc tidak mengenali atau memahami suatu tipe, itu hanya digunakan intsebagai pengganti dalam pesan kesalahan. Ini sangat membingungkan, karena jelas itu gcc yang muncul intdan bukan kode sumbernya. Tapi begitulah adanya.

Barry
sumber
"Bracing-init-list tidak memiliki tipe argumen" tidak jelas bagi saya. std :: pair <int, int> f () {return {1,2}; } berfungsi, dan {1,2} tidak memiliki tipe (ini memanggil konstruktor std :: pair <int, int> seperti yang saya mengerti). Mungkin dengan <auto, auto>, kompiler tidak dapat menyimpulkan tipe 1, dan 2 di daftar initializer {1, 2}?
mfnx
@ mfnx Tidak tidak memiliki argumen tipe , hanya saja tidak memiliki tipe. daftar init yang diperkuat hanya dapat digunakan dalam situasi tertentu - seperti menginisialisasi tipe yang dikenal. Tetapi mereka tidak dapat digunakan dalam deduksi - karena mereka tidak memiliki tipe. Kecuali auto x = {1, 2};berfungsi, tetapi hanya jika semua jenisnya sama.
Barry
2
Kebanyakan kompiler, alih-alih berhenti pada kesalahan pertama, cobalah untuk memulihkannya sehingga mereka dapat melaporkan kesalahan tambahan. Ini umumnya berarti mengasumsikan bahwa segala sesuatu yang tidak dapat diparsing adalah int. Bukan itu intadalah placeholder dalam pesan kesalahan; kompiler benar-benar berpikir bahwa ini adalah int. (Untuk memperjelas ini, gcc mungkin seharusnya mengatakan "mengasumsikan int" di beberapa titik.)
Raymond Chen
2
Perhatikan bahwa kemungkinan jalan lain untuk ekstensi adalah mengizinkan pengurangan argumen templat kelas untuk jenis pengembalian, karena std::pair __f{1,2};berfungsi.
Davis Herring
2
@ Davidviser Saya tidak ingin std::optional f() { return 4; }bekerja.
Barry