การแปลงแลมบ์ดาเป็น std::tr1::function

การใช้ Visual Studio 2008 กับเซอร์วิสแพ็ค tr1 และ Intel C++ Compiler 11.1.071 [IA-32] สิ่งนี้เกี่ยวข้องกับ คำถาม

ฉันกำลังพยายามเขียนแผนที่การทำงานสำหรับ c++ ซึ่งจะทำงานเหมือนกับเวอร์ชัน Ruby

strings = [2,4].map { |e| e.to_s }

ดังนั้นฉันจึงได้กำหนดฟังก์ชันต่อไปนี้ในเนมสเปซ VlcFunctional

template<typename Container, typename U>
vector<U> map(const Container& container, std::tr1::function<U(Container::value_type)> f)
{
    vector<U> transformedValues(container.size());
    int index = -1; 
    BOOST_FOREACH(const auto& element, container)
    {
        transformedValues.at(++index) = f(element);
    }
    return transformedValues; 
}

และคุณสามารถเรียกสิ่งนี้ได้ (โปรดทราบว่าอาร์กิวเมนต์เทมเพลตฟังก์ชันถูกกำหนดไว้อย่างชัดเจน):

vector<int> test;
test.push_back(2); test.push_back(4); 
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, [](int i) -> string
{
    return ToString(i);
});

หรือเป็นเช่นนั้น (โปรดทราบว่าอาร์กิวเมนต์เทมเพลตฟังก์ชันไม่ได้ถูกกำหนดไว้อย่างชัดเจน)

std::tr1::function f = [](int i) -> string { return ToString(i); };
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, f);

แต่ที่สำคัญ ไม่ใช่แบบนี้

vector<string> mappedData2 = VlcFunctional::map(test, [](int i) -> string { return ToString(i); });

หากไม่มีคำจำกัดความที่ชัดเจนของอาร์กิวเมนต์เทมเพลต hte ก็จะไม่ทราบว่าควรใช้เทมเพลตใดและเกิดข้อผิดพลาดในการคอมไพล์

 ..\tests\VlcFunctional_test.cpp(106): error: no instance of function template "VlcFunctional::map" matches the argument list, argument types are: (std::vector<int, std::allocator<int>>, __lambda3)

การกำหนดอาร์กิวเมนต์ของเทมเพลตทำให้ไวยากรณ์มีขนาดใหญ่ขึ้นมาก และฉันกำลังมุ่งเป้าไปที่ไซต์การโทรที่มีปัญหาน้อยที่สุด มีแนวคิดใดบ้างที่ว่าทำไมจึงไม่รู้ว่าจะแปลงได้อย่างไร นี่เป็นปัญหาของคอมไพเลอร์หรือภาษาไม่อนุญาตให้มีการอนุมานอาร์กิวเมนต์เทมเพลตประเภทนี้


person Jamie Cook    schedule 09.09.2010    source แหล่งที่มา
comment
รอง: เพื่อวัตถุประสงค์ด้านความเข้ากันได้ คุณควรใช้ typename Container::value_type ในการประกาศ std::tr1::function Visual นั้นให้อภัยได้ แต่คอมไพเลอร์อื่นไม่ได้เป็นเช่นนั้น   -  person Matthieu M.    schedule 09.09.2010
comment
ตั้งแต่เมื่อใดที่มีการรองรับนิพจน์แลมบ์ดา C ++ 0x ใน VS2008 ตั้งแต่เมื่อใด เท่าที่ฉันรู้พวกเขาเพิ่งเพิ่มเข้ามาใน VS2010 เมื่อเร็ว ๆ นี้   -  person snk_kid    schedule 09.09.2010
comment
ฉันใช้ Visual Studio 2008 รวมกับคอมไพเลอร์ Intel 11.1   -  person Jamie Cook    schedule 09.09.2010
comment
คุณควรเพิ่มคอมไพลเลอร์จริงในประโยคแรกที่คุณระบุสภาพแวดล้อม ในปัจจุบัน การสนับสนุนฟีเจอร์ c++0x ยังไม่สมบูรณ์ กล่าวคือ อย่างน้อยที่สุดก็ในคอมไพเลอร์ต่างๆ และมีนิสัยแปลกๆ บางประการ...   -  person David Rodríguez - dribeas    schedule 09.09.2010


คำตอบ (1)


ปัญหาคือแลมบ์ดาไม่ใช่ std::function แม้ว่าจะสามารถแปลงได้ก็ตาม เมื่ออนุมานอาร์กิวเมนต์ประเภท คอมไพเลอร์ไม่ได้รับอนุญาตให้ทำการแปลงอาร์กิวเมนต์ที่ให้มาจริง ฉันจะหาวิธีให้คอมไพเลอร์ตรวจจับประเภท U และปล่อยให้อาร์กิวเมนต์ที่สองว่างเพื่อให้คอมไพเลอร์อนุมานได้:

template <typename Container, typename Functor>
std::vector< XXX > VlcFunctional::map( Container &, Functor )...

ตอนนี้ปัญหาคือจะเขียนอะไรใน XXX ฉันไม่มีคอมไพเลอร์แบบเดียวกับที่คุณมี และฟีเจอร์ C++0x ทั้งหมดยังค่อนข้างยุ่งยากอยู่เล็กน้อย ก่อนอื่นฉันจะลองใช้ decltype:

template <typename Container, typename Functor>
auto VlcFunctional::map( Container & c, Functor f ) -> std::vector< decltype(f(*c.begin())) > ...

หรืออาจพิมพ์ลักษณะหากคอมไพเลอร์ยังไม่รองรับ decltype

โปรดทราบว่าโค้ดที่คุณกำลังเขียนนั้นค่อนข้างไม่เหมือนกันในภาษา C ++ โดยปกติแล้วเมื่อจัดการกับคอนเทนเนอร์ ฟังก์ชันต่างๆ จะถูกนำไปใช้ในแง่ของตัววนซ้ำ และโดยพื้นฐานแล้วแผนที่ทั้งหมดของคุณจะเป็น std::transform แบบเก่า:

std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<std::string> s;
std::transform( v.begin(), v.end(), std::back_inserter(s), [](int x) { return ToString(x); } );

โดยที่ std::transform คือเวอร์ชัน C++ ของฟังก์ชัน map ของคุณ แม้ว่าไวยากรณ์จะยุ่งยากกว่า แต่ข้อดีก็คือ คุณสามารถนำไปใช้กับคอนเทนเนอร์ใดก็ได้ และสร้างเอาต์พุตไปยังคอนเทนเนอร์อื่น ๆ ได้ ดังนั้นคอนเทนเนอร์ ที่เปลี่ยนรูปแบบ จะไม่ได้รับการแก้ไขเป็น std::vector

แก้ไข: วิธีที่สามซึ่งอาจง่ายกว่าที่จะนำไปใช้กับการสนับสนุนคอมไพเลอร์ปัจจุบันของคุณคือการจัดเตรียมประเภทการส่งคืนของแลมบ์ดาเป็นอาร์กิวเมนต์เทมเพลตด้วยตนเองและปล่อยให้คอมไพเลอร์อนุมานส่วนที่เหลือ:

template <typename LambdaReturn, typename Container, typename Functor>
std::vector<LambdaReturn> map( Container const & c, Functor f )
{
   std::vector<LambdaReturn> ret;
   std::transform( c.begin(), c.end(), std::back_inserter(ret), f );
   return ret;
}
int main() {
   std::vector<int> v{ 1, 2, 3, 4, 5 };
   auto strs = map<std::string>( v, [](int x) {return ToString(x); });
}

แม้ว่าคุณต้องการเพิ่มน้ำตาลเชิงวากยสัมพันธ์ให้กับฟังก์ชัน map ของคุณ ก็ไม่จำเป็นต้องนำไปใช้ด้วยตนเองเมื่อคุณสามารถใช้ฟังก์ชันที่มีอยู่ได้

person David Rodríguez - dribeas    schedule 09.09.2010
comment
เดวิดขอบคุณสำหรับการตอบกลับ ฉันตระหนักถึงฟังก์ชันการแปลงและเป็นการโทรที่ยุ่งยากที่ฉันพยายามหลีกเลี่ยง - ในขณะเดียวกันก็ปรับปรุงความเข้าใจเกี่ยวกับ C++ ของฉันด้วย - person Jamie Cook; 09.09.2010
comment
ฉันไม่แน่ใจจริงๆเกี่ยวกับไวยากรณ์ที่คุณเสนอด้วยวิธี decltype ... ฉันใช้คอมไพเลอร์ intel 11.1 c++ ซึ่งควรจะรองรับ decltype แต่ฉันได้รับข้อผิดพลาดในการคอมไพล์จำนวนมากโดยใช้ไวยากรณ์ของคุณ - ฉันจะหาข้อมูลเพิ่มเติมได้ที่ไหน ข้อมูลเกี่ยวกับวิธีการนี้? - person Jamie Cook; 09.09.2010
comment
@Jamie Cook: ฉันคิดว่าฉันควรจะบอกว่ารองรับประเภทการส่งคืนต่อท้าย ... การอ้างอิงที่อ่านง่ายคือ wikipedia ในมาตรฐาน คุณจะต้องไปที่ส่วน 8.5[dcl.decl]/4 เพื่อดูไวยากรณ์ /5 ระบุกรณีการใช้งาน - person David Rodríguez - dribeas; 09.09.2010
comment
เดวิด ขอบคุณสำหรับความช่วยเหลือของคุณ - ฉันรู้ว่าคอมไพเลอร์ intel c++ ใช้งานได้กับ [auto VariableName = functionCall();] ปกติ แต่ดูเหมือนว่าจะไม่รองรับประเภทการส่งคืน - person Jamie Cook; 09.09.2010
comment
ฉันก็ไม่รู้ด้วยว่าคุณสามารถกำหนดรายการอาร์กิวเมนต์ได้บางส่วน - นี่เป็นวิธีแก้ปัญหาที่สมบูรณ์แบบในขณะนี้ หวังว่าพวก Intel จะได้ลงมือทำและอนุญาตให้ฉันลองใช้วิธีแก้ปัญหา Decltype ของคุณบ้าง - ตอนนี้ฉันพอใจกับสิ่งที่เราคิดขึ้นมา ขอบคุณอีกครั้ง! - person Jamie Cook; 09.09.2010
comment
ฉันได้ลบความคิดเห็นก่อนหน้าแล้ว ฉันไม่สามารถระบุอาร์กิวเมนต์ -std=c++0x ได้ ดังนั้นข้อผิดพลาดที่ฉันรายงานจึงไม่เหมาะสม ยังไม่รองรับประเภทการส่งคืนต่อท้ายและเป็นคุณสมบัติที่ดีที่มี... - person David Rodríguez - dribeas; 09.09.2010