แรงจูงใจในตัวเองสามารถมองเห็นได้ในกระดาษ
ไม่จำเป็นต้องสร้างคอนสตรัคเตอร์อย่างมีเงื่อนไข นั่นคือคุณต้องการ:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
อดีตเป็นเรื่องปกติผู้สร้างเหล่านั้นโดยปริยาย explicit
แต่หลังจะไม่ดีเหล่านั้นมีการก่อสร้าง ด้วย C ++ 17 (หรือ C ++ 20 ที่มีแนวคิด) วิธีเดียวที่จะทำให้งานนี้คือการเขียนตัวสร้างสอง - หนึ่งexplicit
และหนึ่งไม่:
template <typename T1, typename T2>
struct pair {
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2> &&
std::is_convertible_v<U1, T1> &&
std::is_convertible_v<U2, T2>
, int> = 0>
constexpr pair(U1&&, U2&& );
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2> &&
!(std::is_convertible_v<U1, T1> &&
std::is_convertible_v<U2, T2>)
, int> = 0>
explicit constexpr pair(U1&&, U2&& );
};
สิ่งเหล่านี้ซ้ำกันเกือบทั้งหมด - และคำจำกัดความของตัวสร้างเหล่านี้จะเหมือนกัน
ด้วยexplicit(bool)
, คุณสามารถเขียนคอนสตรัคเตอร์เดียวได้ - ด้วยส่วนที่ชัดเจนตามเงื่อนไขของสิ่งก่อสร้างที่แปลเป็น "ตัวระบุ" explicit
:
template <typename T1, typename T2>
struct pair {
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2>
, int> = 0>
explicit(!std::is_convertible_v<U1, T1> ||
!std::is_convertible_v<U2, T2>)
constexpr pair(U1&&, U2&& );
};
สิ่งนี้ตรงกับเจตนาที่ดีกว่าคือมีโค้ดน้อยกว่าในการเขียนและทำงานได้น้อยกว่าสำหรับคอมไพเลอร์ที่ต้องทำในระหว่างการแก้ปัญหาโอเวอร์โหลด
tuple
กับที่ใช้กับคุณลักษณะนี้