T ต้องเป็นประเภทที่สมบูรณ์ที่จะใช้ใน `std :: declval <T>` หรือไม่?


11

ลองพิจารณาตัวอย่างนี้ (มาจากที่นี่ ):

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

มันรวบรวมโดยไม่มีข้อผิดพลาดใน gcc9.2แต่ gcc7.2 และเสียงดังกราว 10.0.0 บ่นเกี่ยวกับการBไม่สมบูรณ์ ข้อผิดพลาดของเสียงดังกราวคือ:

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;
                               ~~~^

1
ชื่อคำถามไม่ตรงกับข้อผิดพลาดใช่หรือไม่ ให้ฉันดูเหมือนว่า GCC .f()บ่นเกี่ยวกับ นั่นทำให้รู้สึก; ชนิดที่ไม่สมบูรณ์ยังไม่ได้เป็นสมาชิกB f
MSalters

@Malters ฉันคิดเหมือนกัน แต่แล้วปัญหาที่แท้จริงที่นี่คืออะไร? ฉันจะสมมติว่าเมื่อคุณได้รับอินสแตนซ์จากstd::declvalมันไม่สำคัญอีกต่อไปถ้าประเภทนั้นเสร็จสมบูรณ์หรือไม่ (และฉันเดาว่าฉันผิดกับ)
idclev 463035818

[expr.ref] / 2 (C ++ 11) กล่าวว่าเกี่ยวกับการเข้าถึงสมาชิกชั้นเรียน: "สำหรับตัวเลือกแรก (dot) การแสดงออกครั้งแรกที่จะมีประเภทระดับที่สมบูรณ์แบบ" และBไม่สมบูรณ์หรือไม่ถือว่าเสร็จสมบูรณ์alias-declarationค่ะ
นักกฎหมายด้านภาษา


1
@LanguageLawyer ตกลงแล้วฉันยอมรับว่าการตีความของฉันปิดและดูเหมือนว่ามีบางสิ่งบางอย่างเปลี่ยนไปตั้งแต่ c ++ 11 ซึ่งทำให้ ok เหนือกว่าในมาตรฐานที่ใหม่กว่า แต่ไม่ใช่ใน c ++ 11 คุณจะช่วยเขียนคำตอบหรือไม่?
idclev 463035818

คำตอบ:


9

แหล่งที่มาของข้อผิดพลาดไม่ใช่std::declvalแต่การเข้าถึงสมาชิกคลาสที่ไม่สมบูรณ์

จนกว่าความละเอียดของCWG1836 จะถูกรวมเข้าด้วยกัน 2.5 ปีที่ผ่านมามาตรฐานนั้นต้องการให้คลาสนั้นเสร็จสมบูรณ์ในนิพจน์การเข้าถึงสมาชิกคลาส ( E1.E2)
[expr.ref] / 2 ใน C ++ 11 :

สำหรับตัวเลือกแรก (จุด) การแสดงออกครั้งแรกจะต้องมีประเภทชั้นที่สมบูรณ์

[expr.ref] / 2 ใน C ++ 17 :

สำหรับตัวเลือกแรก (จุด) การแสดงออกครั้งแรกจะเป็น glvalue ที่มีประเภทชั้นสมบูรณ์

และชั้นจะไม่ถือว่าเป็นครบถ้วนภายในตัวของมันเองalias-declaration [class.mem] / 6 ใน C ++ 17 :member-specification

ชั้นถือว่าเป็นวัตถุชนิดสมบูรณ์ที่กำหนดไว้ ([basic.types]) (หรือพิมพ์เสร็จสมบูรณ์) ที่ปิด}ของชั้นระบุ ภายในคลาสสมาชิก - สเปคคลาสจะถือว่าสมบูรณ์ภายในฟังก์ชันเนื้อหาอาร์กิวเมนต์เริ่มต้นnoexcept-specifier s และสมาชิก initializers เริ่มต้น (รวมถึงสิ่งต่าง ๆ ในคลาสที่ซ้อนกัน) มิฉะนั้นจะถือได้ว่าเป็นที่ไม่สมบูรณ์ภายในชั้นเรียนของตัวเองสมาชิกสเปค


8

จาก[declval] :

ข้อสังเกต: พารามิเตอร์เทมเพลตTของdeclvalอาจเป็นประเภทที่ไม่สมบูรณ์

ถ้อยคำนี้มีอยู่ตั้งแต่ C ++ 11 (ดังนั้นจึงเป็นไปไม่ได้ที่คอมไพเลอร์จะต้องสอดคล้องกับมาตรฐานก่อนหน้านี้)


ยอดเยี่ยมนั่นคือสิ่งที่ฉันหวังไว้ ดูเหมือนว่า gcc จะได้รับการแก้ไขแล้วเสียงดังกราวไม่ (ยัง)
idclev 463035818

@ ก่อนหน้านี้ที่รู้จัก _463035818: ความคิดแรกของฉันคือว่าTควรเป็นประเภทที่สมบูรณ์ ดีใจที่ฉันตรวจสอบมาตรฐาน
AndyG
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.