ผมเฝ้าดูการพูดคุยวอลเตอร์บราวน์ที่ Cppcon14 เกี่ยวกับการเขียนโปรแกรมแม่แบบที่ทันสมัย ( Part I , Part II ) ที่เขานำเสนอของเขาvoid_t
เทคนิค SFINAE
ตัวอย่าง:
กำหนดเทมเพลตตัวแปรแบบง่ายที่ประเมินvoid
ว่าถ้าอาร์กิวเมนต์เท็มเพลตทั้งหมดมีรูปแบบที่ดี:
template< class ... > using void_t = void;
และลักษณะต่อไปนี้ที่ตรวจสอบการมีอยู่ของตัวแปรสมาชิกที่เรียกว่าสมาชิก :
template< class , class = void >
struct has_member : std::false_type
{ };
// specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };
ฉันพยายามเข้าใจว่าเพราะอะไรและทำงานอย่างไร ดังนั้นตัวอย่างเล็ก ๆ :
class A {
public:
int member;
};
class B {
};
static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );
1 has_member< A >
has_member< A , void_t< decltype( A::member ) > >
A::member
ที่มีอยู่decltype( A::member )
มีรูปแบบที่ดีvoid_t<>
ถูกต้องและประเมินผลถึงvoid
has_member< A , void >
และดังนั้นจึงเลือกเทมเพลตพิเศษhas_member< T , void >
และประเมินผลให้true_type
2 has_member< B >
has_member< B , void_t< decltype( B::member ) > >
B::member
ไม่ได้อยู่decltype( B::member )
มีรูปแบบไม่ดีและล้มเหลวอย่างเงียบ ๆ (sfinae)has_member< B , expression-sfinae >
เทมเพลตนี้จึงถูกยกเลิก
- คอมไพเลอร์พบ
has_member< B , class = void >
กับโมฆะเป็นอาร์กิวเมนต์เริ่มต้น has_member< B >
ประเมินให้false_type
คำถาม:
1. ความเข้าใจของฉันเกี่ยวกับสิ่งนี้ถูกต้องหรือไม่?
2. วอลเตอร์บราวน์ระบุว่าอาร์กิวเมนต์เริ่มต้นจะต้องเป็นชนิดเดียวกันกับที่ใช้ในvoid_t
การทำงาน ทำไมถึงเป็นอย่างนั้น? (ฉันไม่เห็นว่าทำไมประเภทนี้ต้องตรงกันไม่ใช่แค่งานประเภทเริ่มต้นเท่านั้น)
has_member< T , class = void >
ผิดนัดในvoid
เลย สมมติว่าคุณสมบัตินี้จะใช้กับอาร์กิวเมนต์ 1 เทมเพลตเมื่อใดก็ได้ดังนั้นอาร์กิวเมนต์เริ่มต้นอาจเป็นประเภทใดก็ได้
template <class, class = void>
template <class, class = void_t<>>
ดังนั้นตอนนี้เรามีอิสระที่จะทำสิ่งที่เราต้องการด้วยvoid_t
การดำเนินนามแฝงแม่แบบ :)
has_member<A,int>::value
ลองนึกภาพยืนยันคงเขียนเป็น: จากนั้นความเชี่ยวชาญเฉพาะบางส่วนที่ประเมินว่าhas_member<A,void>
ไม่สามารถจับคู่ได้ ดังนั้นจะต้องมีหรือมีน้ำตาลประโยคอาร์กิวเมนต์เริ่มต้นของประเภทhas_member<A,void>::value
void