ฟังก์ชัน get_from_variant ใน C ++
นี่คือคำถามติดตามสำหรับฟังก์ชัน recursive_transform สำหรับประเภทต่างๆที่ซ้อนกัน Iterable ด้วยมาตรฐาน :: การดำเนินงานที่แตกต่างกันใน C ในฐานะที่เป็นคำตอบของ G. Sliepenกล่าวเหลือเพียงซ้ำเปลี่ยนการดำเนินงานrecursive_transform()อาจจะเป็นความคิดที่ดี ดังนั้นการนำrecursive_transformฟังก์ชันไปใช้งานจึงถูกเก็บไว้ในรูปแบบต่อไปนี้ ยิ่งไปกว่านั้นการประกาศล่วงหน้าได้ถูกลบออก
template<class T, class _Fn> requires is_iterable<T>
static inline T recursive_transform(const T input, _Fn func)
{
T returnObject = input;
std::transform(input.begin(), input.end(), returnObject.begin(), func);
return returnObject;
}
template<class T, class _Fn> requires is_iterable<T> && is_element_iterable<T>
static inline T recursive_transform(const T input, _Fn func)
{
T returnObject = input;
std::transform(input.begin(), input.end(), returnObject.begin(),
[func](const auto& element)
{
return recursive_transform(element, func);
}
);
return returnObject;
}
อย่างไรก็ตามฉันยังคงต้องการจัดการโครงสร้างสารประกอบที่มีช่วงและstd::variantเช่นstd::vector<std::variant<double>>. ฟังก์ชั่นใหม่get_from_variantเกิดขึ้นในความคิดของฉันเพื่อมุ่งเน้นไปที่การดำเนินการกับสิ่งเหล่านี้
template<typename T_variant, typename T>
static inline auto get_from_variant(T_variant input_variant)
{
T return_val;
std::visit([&](auto&& arg)
{
return_val = static_cast<T>(arg);
return arg;
},
input_variant);
return return_val;
}
การทดสอบget_from_variantฟังก์ชันนี้:
int main()
{
// get_from_variant function test
std::variant<double> testNumber = 1;
std::cout << get_from_variant<decltype(testNumber), double>(testNumber);
// The usage of recursive_transform function and get_from_variant function
std::variant<double> variant_number = 3.14;
std::vector<decltype(variant_number)> testVector1;
testVector1.push_back(variant_number);
testVector1.push_back(variant_number);
testVector1.push_back(variant_number);
std::cout << get_from_variant<std::variant<double>, double>(recursive_transform(testVector1, [](auto x){ return get_from_variant<std::variant<double>, double>(x) + 1; }).at(0)) << std::endl;
return 0;
}
ยินดีรับข้อเสนอแนะทั้งหมด
คำถามใดที่ต้องติดตาม?
ฟังก์ชัน recursive_transform สำหรับประเภทต่างๆที่ซ้อนกันซ้ำได้ด้วย std :: variant Implementation ใน C ++
มีการเปลี่ยนแปลงอะไรในโค้ดตั้งแต่คำถามสุดท้าย
เพื่อที่จะจัดการกับโครงสร้างสารประกอบที่มีช่วงและ
std::variantเช่นstd::vector<std::variant<double>>ในทางที่ดี, ฟังก์ชั่นใหม่get_from_variantได้ถูกสร้างขึ้นเหตุใดจึงมีการขอรีวิวใหม่
ในความคิดของฉันไม่แน่ใจว่าการออกแบบฟังก์ชั่น
get_from_variantนั้นดีหรือไม่? ไอเดียหรือการใช้งานดีหรือไม่? ยินดีต้อนรับทุกความคิดเห็น
คำตอบ
ฉันไม่ได้ติดตามกระทู้นี้มาตั้งแต่ต้นดังนั้นฉันจึงสับสนมากกว่าที่คุณคิดว่าผู้อ่านจะมาถึงจุดนี้ เป็นความคิดที่ดีสำหรับคุณที่จะให้ตัวอย่างที่รวบรวมได้อย่างสมบูรณ์ทุกครั้งแม้จะเป็นลิงก์ Godbolt ก็ตามหากคุณต้องการให้คำถามมุ่งเน้นไปที่โค้ดเพียงเล็กน้อย
อันที่จริงฉันชอบที่จะเห็นลิงค์ Godbolt ( นอกเหนือจากการดูโค้ดในคำถามตามที่คุณได้ทำอย่างถูกต้อง) เนื่องจากมันช่วยให้ฉันประหยัดปัญหาในการวางรหัสของคุณลงใน Godbolt ด้วยตัวเอง :) นี่คือเชื่อมโยงไปยังรหัสของคุณ: Godbolt
std::variant<double> testNumber = 1;
สิ่งนี้ไม่ได้รวบรวมใน C ++ 20 มันใช้เพื่อ? ถ้าเป็นเช่นนั้นนั่นเป็นการแบ่ง API ที่ค่อนข้างใหญ่สำหรับ C ++ ... แต่ไม่ใช่ปัญหาของคุณ อย่างไรก็ตามให้เปลี่ยน1.0และคอมไพล์ใหม่
template<typename T_variant, typename T>
static inline auto
สูญเสียstatic inline. เทมเพลตอินไลน์ตามคำจำกัดความได้อย่างมีประสิทธิภาพและคุณไม่ต้องการให้เทมเพลตนี้เป็นแบบคงที่ - คุณไม่ต้องการบังคับให้หน่วยการแปลแต่ละหน่วยเก็บสำเนาเฉพาะของตัวเองไว้ (ในกรณีที่อินไลน์เนอร์ไม่ได้รับการปรับให้เหมาะสมที่สุด)
ฉันไม่ได้เป็นแฟนของGiraffe_case. ชื่อพารามิเตอร์เทมเพลตควรสั้นและCamelCase; Vที่นี่ผมขอแนะนำให้
ของคุณแลมบ์ดามีที่ไร้ประโยชน์std::visit return arg;อันที่จริงฟังก์ชันทั้งหมดนี้ควรมีลักษณะดังนี้
template<class V, class T>
auto get_from_variant(V input) {
return std::visit([&](auto&& arg) {
return static_cast<T>(arg);
}, input);
}
เมื่อลบส่วนที่ถูกลบออกเรามีเซลล์สมองที่มีอิสระที่จะมุ่งเน้นไปที่ระดับถัดไปของการอวดรู้: คุณกำลังรับargการอ้างอิงการส่งต่อ ( auto&&) แต่คุณไม่ได้ส่งต่อไปยังไฟล์static_cast. บางทีเราควรจะใช้ตรงstatic_cast<T>(static_cast<decltype(arg)>(arg))นี้เพื่อที่ว่าถ้าargเป็นการอ้างอิง rvalue มันจะถูกย้ายไปอยู่ในคอนTสตรัคเตอร์ของ?
แต่เดี๋ยวก่อน; argจะไม่อ้างอิง rvalue เพราะเรากำลังไปที่ lvalue input! ดังนั้นบางทีเราไม่ควรคาดหวังว่าจะแก้ไขสิ่งที่argเราเยี่ยมชม - เราสามารถใช้มันconst auto& argได้ แต่ถ้าเราไม่ได้คาดหวังที่จะปรับเปลี่ยนinputบางทีมันควรจะนำชีวินใช่รอสักครู่เรามีเหตุผลที่ทำสำเนาของinputที่นี่? เพียงแค่ใช้การอ้างอิง const เพื่อเริ่มต้นด้วย!
template<class V, class T>
auto get_from_variant(const V& input) {
return std::visit([](const auto& arg) {
return static_cast<T>(arg);
}, input);
}
ฉันทิ้ง[&]แลมด้าไปแล้วเพราะมันไม่ต้องการการจับใด ๆ
เราควรดูพารามิเตอร์เทมเพลตget_from_variantด้วย Vสามารถอนุมานได้และTไม่สามารถ; มันสมเหตุสมผลเสมอที่จะใส่พารามิเตอร์ที่ไม่สามารถหักล้างได้ก่อนเสมอ
template<class T, class V>
auto get_from_variant(const V& input) {
return std::visit([](const auto& arg) {
return static_cast<T>(arg);
}, input);
}
ตอนนี้คนขับหลักของเรามีลักษณะเช่นนี้ :
std::variant<double> testNumber = 1.0;
std::cout << get_from_variant<double>(testNumber);
std::vector testVector1 = {
std::variant<double>(3.14),
std::variant<double>(3.14),
std::variant<double>(3.14),
};
std::cout << get_from_variant<double>(
recursive_transform(testVector1, [](const auto& x){
return get_from_variant<double>(x) + 1;
}).at(0)
) << std::endl;
ในขณะเดียวกันในrecursive_transformคุณจะมีการพิมพ์ผิด: เมื่อคุณมีความหมายconst T input คุณสามารถ grep โดยอัตโนมัติสำหรับการพิมพ์ผิดเหล่านี้และคุณควร!const T& input
อีกครั้งลบ
static inlineจากเทมเพลตชื่อ
_Fnนี้สงวนไว้สำหรับการนำไปใช้งาน เพียงใช้F.funcไม่จำเป็นต้องคัดลอกลงในแลมด้า คุณควรใช้[&]เป็นค่าเริ่มต้นสำหรับแลมบ์ดาทุกตัวที่คุณเขียน (เว้นแต่ว่าข้างต้นคุณสามารถหลีกเลี่ยงได้[])ตามจริงแล้วเว้นแต่คุณจะบ้าคลั่งเกี่ยวกับการทำตามสำนวน STL เพียงแค่ส่งการติดต่อกลับ
Fโดยการอ้างอิง const และหลีกเลี่ยงการคัดลอก มีสถานที่ใน C ++ สำหรับการเรียกกลับแบบ stateful และไม่แน่นอน แต่transformไม่ใช่ที่นั้นกรณีพื้นฐานของคุณมีความซับซ้อนมากกว่าที่จำเป็น มาแก้ไขกัน
วางมันทั้งหมดเข้าด้วยกัน:
template<class T, class F>
T recursive_transform(const T& input, const F& f) {
return f(input);
}
template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
T returnObject = input;
std::transform(input.begin(), input.end(), returnObject.begin(),
[&](const auto& element) {
return recursive_transform(element, f);
}
);
return returnObject;
}
แล้วสำหรับฉันจริงๆแล้วดูเหมือนว่าการใช้std::transformที่นี่จะมากเกินไป: มันอ่านจากinput สองครั้งหนึ่งครั้งเพื่อทำสำเนาและอีกครั้งเพื่อทำการแปลง สมมติว่าเราแค่เปิดโค้ดแบบนี้?
template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
T output = input;
for (auto&& elt : output) {
elt = recursive_transform(elt, f);
}
return output;
}
แน่นอนว่าเราสามารถใช้ C ++ 20 Ranges เพื่อทำสิ่งนี้ได้ :
template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
auto transformed = input | std::views::transform([&](auto&& x) {
return recursive_transform(x, f);
});
return T(transformed.begin(), transformed.end());
}
การคอมไพล์และสร้างโค้ดที่ใหญ่กว่านั้นช้ากว่า - แต่มันอาจเร็วกว่าในรันไทม์หากT::value_typeมีราคาแพงในการคัดลอกเนื่องจากเรากำลังกำจัดการกำหนดสำเนาT::value_type- เรากำลังสร้างโดยตรงในสถานที่