การโทรไปแลมบ์ดานั้นไม่ชัดเจนแม้จะระบุประเภทการคืนอย่างชัดเจน


11

ฟังก์ชั่นโอเวอร์โหลดควรใช้ทั้งฟังก์ชั่นในเนื่องจากประเภทของแลมบ์ดานั้นสามารถนำกลับมาใช้ใหม่ได้std::function(โปรดแก้ไขให้ฉันถ้าฉันผิด) คำถามคือ: ทำไมมีข้อผิดพลาดในการคอมไพล์ด้านล่าง กำหนดไว้หรือไม่ ( [&]() -> Type {})

โปรดทราบว่าสำหรับวิธีแก้ปัญหาปัจจุบันของฉันฉันต้องการการดักจับโดยอ้างอิงนั่นคือสาเหตุที่รหัสประกอบด้วยตรรกะสำหรับมัน

ตัวอย่างต่อไปนี้อธิบายถึงปัญหา:

#include <iostream>
#include <string>    
#include <functional>

void do_some(std::function<void(int)> thing) 
{
   thing(5);
}

void do_some(std::function<bool(int)> thing)
{
   if (thing(10)) 
   {
      std::cout << "it's true!" << std::endl;
   }
}

int main()
{
   int local_to_be_modified = 0;
   do_some(
      [&](int in)
      {
         local_to_be_modified = in;
         std::cout << "This is void-" << std::endl;
      }
   );
   do_some(
      [&](int in) -> bool
      { 
         // error: call to 'do_some' is ambiguous
         local_to_be_modified += in;
         std::cout << "This is bool-" << std::endl;
         return true;
      }
   );
}

6
เพราะstd::function<void(int)>สามารถสร้างได้จากแลมบ์ดาที่ส่งคืนบางสิ่งบางอย่าง (ซึ่งทำให้ค่าส่งคืนถูกละเว้น)
HolyBlackCat

1
นอกเหนือจากนั้นการระบุประเภทการส่งคืนของแลมบ์ดานั้นก็ไม่ได้ทำอะไรเลย
Deduplicator

คำตอบ:


8

เนื่องจากการกลับแลมบ์ดาครั้งที่ 2 boolสามารถเปลี่ยนเป็นทั้งสองstd::function<void(int)>และstd::function<bool(int)>โดยปริยาย

std::function มี Constructor ที่แปลง:

template< class F >
function( F f );

คอนสตรัคนี้ไม่ได้มีส่วนร่วมในการแก้ปัญหาการโอเวอร์โหลดเว้นแต่ว่า f เป็นCallableสำหรับประเภทอาร์กิวเมนต์ Args ... และส่งคืนประเภท R (ตั้งแต่ C ++ 14)

ในฐานะที่เป็นความหมายของCallable ,

นิพจน์ต่อไปนี้ต้องถูกต้อง:

INVOKE<R>(f, std::declval<ArgTypes>()...)

โดยที่INVOKE (f, t1, t2, ... , tN)ถูกกำหนด static_cast<void>(INVOKE(f, t1, t2, ..., tN))ว่า R อาจมีคุณสมบัติเป็น cv voidหรือไม่เช่นนั้นINVOKE (f, t1, t2, ... , tN)จะถูกแปลงเป็น R โดยปริยาย

โปรดทราบว่าแลมบ์ดาที่ 2 ที่ส่งคืนboolสำหรับการstd::function<void(int)>ดังที่แสดงไว้ด้านบนstatic_cast<void>(INVOKE(f, t1, t2, ..., tN))เป็นนิพจน์ที่ถูกต้อง (การส่งคืนboolจะถูกแปลงเป็นvoid) จากนั้นมันก็สามารถแปลงเป็นstd::function<void(int)>ปริยายและทำให้เกิดปัญหาความกำกวม


6

คุณสามารถระบุstatic_castแลมบ์ดาให้เป็นประเภทที่เหมาะสมได้อย่างชัดเจน

using FunBoolRet = std::function<bool(int)>;

do_some(static_cast<FunBoolRet >([&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }));

หรือเก็บแลมบ์ดาให้เป็นstd::function<bool(int)>ประเภทที่เหมาะสมและส่งผ่านไปยังฟังก์ชั่น (ถ้าdo_some(lmda)ควรเรียกหลายครั้ง)

FunBoolRet lmda = [&](int in)
{
    local_to_be_modified += in;
    std::cout << "This is bool-" << std::endl;
    return true;
};    
do_some(lmda); // pass the lambda

หรือตามที่@ MaxLanghof แนะนำเพียงแค่สร้างstd::function<bool(int)>จากแลมบ์ดาในระหว่างการเดินทาง

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});

คุณสามารถข้ามstatic_castและเพียงสร้างstd::functionโดยตรงจากมัน นั่นคือทั้งหมดที่เกิดขึ้นระหว่างการแปลงโดยนัย
Max Langhof

ประเด็นของฉันคือคุณสามารถลบstatic_cast<และสุดท้าย>และมันจะทำสิ่งเดียวกัน แต่มีการพิมพ์น้อย ไม่ต้องการบรรทัดหรืออะไรเพิ่มเติม godbolt.org/z/fQTqF4
Max Langhof
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.