Apakah T harus merupakan tipe yang lengkap untuk digunakan dalam `std :: declval <T>`?

11

Pertimbangkan contoh ini (datang dari sini ):

#include <type_traits>
#include <iostream>
template <typename U>
struct A {
};

struct B {
   template <typename F = int>
   A<F> f() { return A<F>{}; }

   using default_return_type = decltype(std::declval<B>().f());
};

int main()
{
    B::default_return_type x{};
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
}

Ini mengkompilasi tanpa kesalahan pada gcc9.2 tetapi gcc7.2 dan dentang 10.0.0 mengeluh tentang Btidak lengkap. Kesalahan dentang adalah:

prog.cc:11:58: error: member access into incomplete type 'B'
   using default_return_type = decltype(std::declval<B>().f());
                                                         ^
prog.cc:7:8: note: definition of 'B' is not complete until the closing '}'
struct B {
       ^
prog.cc:16:8: error: no type named 'default_return_type' in 'B'
    B::default_return_type x{};
    ~~~^
prog.cc:17:35: error: no member named 'default_return_type' in 'B'
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
                               ~~~^
idclev 463035818
sumber
1
Judul pertanyaan tampaknya tidak cocok dengan kesalahan? Bagi saya, kelihatannya GCC mengeluh .f(). Itu masuk akal; tipe Btidak lengkap tidak memiliki anggota f.
MSalters
@Malters saya pikir sama, tapi lalu apa masalah sebenarnya di sini? Saya akan berasumsi bahwa setelah Anda mendapatkan contoh dari std::declvalitu tidak masalah lagi jika jenisnya selesai atau tidak (dan saya kira saya salah dengan itu)
idclev 463035818
[expr.ref] / 2 (C ++ 11) mengatakan tentang akses anggota kelas: "Untuk opsi pertama (titik) ekspresi pertama harus memiliki tipe kelas lengkap" . Dan Btidak lengkap atau dianggap lengkap dalam alias-declaration.
Pengacara Bahasa
@LanguageLawyer Saya tidak menemukan kalimat yang Anda kutip, tetapi hanya "Tipe kelas akan lengkap kecuali akses anggota kelas muncul dalam definisi kelas itu"
idclev 463035818
1
@LanguageLawyer ok maka saya setuju bahwa interpretasi saya tidak aktif dan sepertinya ada sesuatu yang berubah sejak c ++ 11 yang membuat ok di atas dalam standar yang lebih baru tetapi tidak di c ++ 11. Maukah Anda menulis jawaban?
idclev 463035818

Jawaban:

9

Sumber kesalahan tidak std::declval, tetapi akses anggota kelas tidak lengkap.

Sampai resolusi CWG1836 digabung 2,5 tahun yang lalu, standar mengharuskan kelas untuk melengkapi ekspresi akses anggota kelas ( E1.E2).
[expr.ref] / 2 dalam C ++ 11 :

Untuk opsi pertama (titik) ekspresi pertama harus memiliki tipe kelas lengkap.

[expr.ref] / 2 dalam C ++ 17 :

Untuk opsi pertama (titik) ekspresi pertama harus berupa glvalue yang memiliki tipe kelas lengkap.

Dan sebuah kelas tidak dianggap lengkap di alias-declarationdalam kelasnya sendiri member-specification.
[class.mem] / 6 dalam C ++ 17 :

Sebuah kelas dianggap sebagai benar-didefinisikan jenis objek ([basic.types]) (atau jenis lengkap) pada penutupan }dari kelas-specifier . Di dalam spesifikasi anggota kelas , kelas dianggap sebagai lengkap di dalam badan fungsi, argumen default, noexcept-specifier , dan inisialisasi anggota standar (termasuk hal-hal tersebut di kelas bersarang). Kalau tidak, dianggap tidak lengkap dalam spesifikasi anggota kelasnya sendiri .

Pengacara Bahasa
sumber
8

Dari [deklarasi] :

Keterangan: Parameter Template Tdari declvalmungkin merupakan jenis yang tidak lengkap.

Kata-kata ini telah ada sejak C ++ 11 (jadi tidak mungkin bagi kompiler untuk menyesuaikan diri dengan standar sebelumnya)

AndyG
sumber
Luar biasa, itulah yang saya harapkan. Sepertinya gcc sudah memperbaikinya, dentang belum (belum)
idclev 463035818
@ formerlyknownas_463035818: Pikiran pertama saya adalah bahwa itu Tharus benar-benar tipe yang lengkap. Senang saya memeriksa standar.
AndyG