recursive_transform สำหรับ std :: vector ที่มีประเภท return ต่างๆ
นี่คือคำถามติดตามสำหรับฟังก์ชัน recursive_transform สำหรับประเภทต่างๆที่ซ้อนกัน Iterable ด้วยมาตรฐาน :: การดำเนินงานที่แตกต่างกันใน C ++และฟังก์ชั่น get_from_variant ใน C ขอขอบคุณG. SliepenและQuuxplusoneให้คำแนะนำการตรวจสอบโดยละเอียด อย่างไรก็ตามเวอร์ชันของrecursive_transformฟังก์ชันที่มีอยู่จะถือว่าประเภทการส่งคืนจะเหมือนกับประเภทอินพุตเสมอ กล่าวอีกนัยหนึ่งมันทำงานได้ดีกับฟังก์ชันแลมบ์ดาเช่น[](double x)->double { return x + 1; }(ประเภทของทั้งอินพุตและเอาต์พุตคือdouble) หรือ[](int x)->int { return x + 1; }(ประเภทของทั้งอินพุตและเอาต์พุตคือint) ในขั้นตอนต่อไปฉันต้องการมุ่งเน้นไปที่กรณีที่ประเภทการส่งคืนแตกต่างจากประเภทอินพุต ตัวอย่างเช่น,[](int x)->std::string { return std::to_string(x); }. เพราะแหล่งที่มาประเภทการกลับมาของrecursive_transformที่ระบุไว้ในTก็ไม่สามารถจัดการกับกรณีที่ประเภทของการส่งออกแปรรูปจากฟังก์ชั่นแลมบ์ดาที่แตกต่างจากf Tประเภทการเปลี่ยนแปลง Let 's Tเข้ามาautoดังต่อไปนี้ นี้autoไวยากรณ์ที่ใช้ที่นี่ทำให้ประเภทอันเกิดการปรับตัว
template<class T, class F>
auto recursive_transform(const T& input, const F& f) {
return f(input);
}
อีกส่วนหนึ่งrecursive_transformคือโครงสร้างแบบวนซ้ำและมีความซับซ้อนมากกว่าด้านบน ระบุคอนเทนเนอร์ไว้std::vectorที่นี่ก่อน
template<class T, class F> requires is_iterable<T>
auto recursive_transform(const T& input, const F& f) {
typedef typename std::iterator_traits<typename T::iterator>::value_type
value_type;
std::vector<decltype(recursive_transform(std::declval<value_type&&>(), f))> output(input.size());
std::transform(input.begin(), input.end(), output.begin(),
[f](auto& element)
{
return recursive_transform(element, f);
}
);
return output;
}
recursive_transformการทดสอบการทำงานของแม่แบบดังกล่าวข้างต้น
std::vector<int> test_vector = {
1, 2, 3
};
auto recursive_transform_result = recursive_transform(
test_vector,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result.at(0) << std::endl; // recursive_transform_result.at(0) is a std::string
std::vector<decltype(test_vector)> test_vector2 = {
test_vector, test_vector, test_vector
};
auto recursive_transform_result2 = recursive_transform(
test_vector2,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result2.at(0).at(0) << std::endl; // recursive_transform_result.at(0).at(0) is also a std::string
ยินดีรับข้อเสนอแนะทั้งหมด
คำถามใดที่ต้องติดตาม?
มีการเปลี่ยนแปลงอะไรในโค้ดตั้งแต่คำถามสุดท้าย
ใน
recursive_transformฟังก์ชันเวอร์ชันก่อนหน้านี้จะทำงานได้ดีเมื่อประเภทการส่งคืนเหมือนกับประเภทอินพุต แนวคิดหลักในคำถามนี้คือการพยายามใช้เวอร์ชันเพิ่มเติมซึ่งประเภทการส่งคืนจะแตกต่างจากประเภทอินพุตเหตุใดจึงมีการขอรีวิวใหม่
recursive_transformฟังก์ชันเวอร์ชันก่อนหน้าถือว่าประเภทการส่งคืนจะเหมือนกับประเภทอินพุตเสมอ ฉันพยายามมุ่งเน้นไปที่กรณีที่ประเภทการส่งคืนแตกต่างจากประเภทอินพุตเพื่อให้ฟังก์ชันทั่วไปมากขึ้น อย่างไรก็ตามฉันรู้ว่าฉันทำให้อัลกอริทึมของฉันมีความเชี่ยวชาญมากขึ้นstd::vectorอีกครั้งในโค้ดเวอร์ชันนี้ ฉันไม่รู้ว่าจะใช้recursive_transformฟังก์ชันทั่วไปได้อย่างไรทั้งในประเภทเอาต์พุตต่างๆและประเภทคอนเทนเนอร์ต่างๆด้วยวิธีที่ง่ายและชาญฉลาด หากมีข้อเสนอแนะหรือแนวคิดที่เป็นไปได้เกี่ยวกับเรื่องนี้โปรดแจ้งให้เราทราบ
คำตอบ
ฉันกลัวว่าความรู้ของฉันเกี่ยวกับเทมเพลตใน C ++ จะถึงขีด จำกัด แต่ฉันจะพยายามตอบให้ดีที่สุดเท่าที่จะทำได้:
ใช้std::back_inserter()เติมเวกเตอร์
แทนที่จะสร้างเวกเตอร์ในขนาดที่กำหนดเพียงแค่ประกาศเวกเตอร์ว่าง แต่สำรองความจุให้เพียงพอแล้วใช้std::back_inserter()เพื่อเติม:
std::vector<decltype(recursive_transform(std::declval<value_type&&>(), f))> output;
output.reserve(input.size());
std::transform(input.begin(), input.end(), std::back_inserter(output),
[f](auto& element)
{
return recursive_transform(element, f);
}
);
อย่างไรก็ตามหากคุณไม่ได้ใช้std::vectorแต่คอนเทนเนอร์ประเภทอื่นreserve()และ / หรือstd::back_inserter()อาจไม่เหมาะสม
การกำหนดประเภทคอนเทนเนอร์
ดังนั้นความนึกคิดเราไม่ต้องการที่จะประกาศแต่ภาชนะด้านนอกของประเภทstd::vector Tคุณสามารถใช้พารามิเตอร์เทมเพลตเทมเพลตเพื่อแยกโครงสร้างประเภทเทมเพลต:
template<template<class> class Container, class ValueType, class Function>
requires is_iterable<Container<ValueType>>
auto recursive_transform(const Container<ValueType> &input, const Function &f)
{
// You want to be able to write this:
using TransformedValueType = decltype(recursive_transform(*input.begin(), f));
Container<TransformedValueType> output;
...
}
น่าเสียดายที่ไม่ได้ผลอย่างน้อยก็ไม่ใช่กับเสียงดังเพราะstd::vectorจริงๆแล้วมีพารามิเตอร์เทมเพลตสองรายการและคอนเทนเนอร์อื่น ๆ อาจมีพารามิเตอร์เทมเพลตมากหรือน้อย ดังนั้นวิธีแก้ปัญหานั้นคือการประกาศValueTypeเป็นชุดพารามิเตอร์เทมเพลต:
template<template<class...> class Container, class Function, class Ts...>
requires is_iterable<Container<Ts...>>
auto recursive_transform(const Container<Ts...> &input, const Function &f)
{
using TransformedValueType = decltype(recursive_transform(*input.begin(), f));
Container<TransformedValueType> output;
...
}
แม้ว่าจะไม่ส่งต่อพารามิเตอร์เทมเพลตที่สองก็ตาม รวมทุกอย่างไว้ด้วยกัน:
template<typename T>
concept is_iterable = requires(T x)
{
*std::begin(x);
std::end(x);
};
template<class T, class Function>
auto recursive_transform(const T &input, const Function &f)
{
return f(input);
}
template<template<class...> class Container, class Function, class... Ts>
requires is_iterable<Container<Ts...>>
auto recursive_transform(const Container<Ts...> &input, const Function &f)
{
using TransformedValueType = decltype(recursive_transform(*input.begin(), f));
Container<TransformedValueType> output;
std::transform(std::begin(input), std::end(input), std::back_inserter(output),
[&](auto &element)
{
return recursive_transform(element, f);
}
);
return output;
}