ปัญหาคือที่นี่เนื่องจากคลาสถูกเทมเพลตT
ในคอนสตรัคเตอร์Foo(T&&)
เราไม่ได้ทำการลดการพิมพ์ เรามีการอ้างอิงค่า r เสมอ นั่นคือคอนสตรัคสำหรับที่Foo
มีลักษณะเช่นนี้:
Foo(int&&)
Foo(2)
ทำงานเพราะ2
เป็นที่แพร่หลาย
Foo(x)
ไม่ได้เพราะx
เป็น lvalue int&&
ที่ไม่สามารถเชื่อมโยงกับ คุณสามารถทำได้std::move(x)
เพื่อแปลงเป็นประเภทที่เหมาะสม ( สาธิต )
Foo<int&>(x)
ทำงานได้ดีเพราะตัวสร้างกลายเป็นFoo(int&)
เพราะการอ้างอิงกฎการยุบ เริ่มแรกมันFoo((int&)&&)
ยุบตัวลงFoo(int&)
ตามมาตรฐาน
ในส่วนที่เกี่ยวกับคำแนะนำการหัก "ซ้ำซ้อน" ของคุณ: เริ่มแรกมีคำแนะนำการหักแม่แบบเริ่มต้นสำหรับรหัสที่โดยทั่วไปทำหน้าที่เหมือนฟังก์ชันผู้ช่วยดังนี้
template<typename T>
struct Foo {
Foo(T&&) {}
};
template<typename T>
Foo<T> MakeFoo(std::add_rvalue_reference_t<T> value)
{
return Foo<T>(std::move(value));
}
//...
auto f = MakeFoo(x);
นี่เป็นเพราะมาตรฐานกำหนดว่าวิธีแม่แบบนี้ (ตัวละคร) มีพารามิเตอร์เทมเพลตเดียวกับคลาส (เพิ่งT
) ตามด้วยพารามิเตอร์แม่แบบใด ๆ ที่เป็นตัวสร้าง (ไม่มีในกรณีนี้ตัวสร้างไม่ได้สร้างเทมเพลต) จากนั้นชนิดของพารามิเตอร์ฟังก์ชั่นจะเหมือนกันกับที่อยู่ในตัวสร้าง ในกรณีของเราหลังจาก instantiating คอนFoo<int>
สตรัคดูเหมือนFoo(int&&)
การอ้างอิง rvalue ในคำอื่น ๆ ดังนั้นการใช้งานของadd_rvalue_reference_t
ข้างต้น
เห็นได้ชัดว่านี่ใช้งานไม่ได้
เมื่อคุณเพิ่มคู่มือการหัก "ซ้ำซ้อน" ของคุณ:
template<typename T>
Foo(T&&) -> Foo<T>;
คุณได้รับอนุญาตให้คอมไพเลอร์ที่จะแยกแยะว่าแม้จะมีชนิดของการอ้างอิงที่แนบมากับT
ในคอนสตรัค ( int&
, const int&
หรือint&&
อื่น ๆ ) คุณตั้งใจประเภทอนุมานสำหรับชั้นเรียนที่จะเป็นโดยการอ้างอิง (เพียงT
) นี่เป็นเพราะเรากำลังทำการอนุมานประเภท
ตอนนี้เราสร้างฟังก์ชันตัวช่วยอีกตัวหนึ่ง (ตัวละคร) ที่มีลักษณะดังนี้:
template<class U>
Foo<U> MakeFoo(U&& u)
{
return Foo<U>(std::forward<U>(u));
}
// ...
auto f = MakeFoo(x);
(การเรียกของเราไปยังตัวสร้างจะถูกเปลี่ยนเส้นทางไปยังฟังก์ชันตัวช่วยสำหรับจุดประสงค์ของการลดอาร์กิวเมนต์เทมเพลตคลาสจึงFoo(x)
กลายเป็นMakeFoo(x)
)
สิ่งนี้จะช่วยให้U&&
กลายเป็นint&
และT
ง่ายint