Panggilan ke lambda tidak jelas meskipun secara eksplisit menyatakan jenis pengembalian

11

Fungsi kelebihan beban harus mengambil kedua functors, mengingat jenis lambda adalah decidable (castable to a std::function (tolong perbaiki saya jika saya salah). Pertanyaannya adalah: Mengapa ada kesalahan kompilasi di bawah, meskipun jenis lambda secara eksplisit didefinisikan? ( [&]() -> Type {})

Harap dicatat, bahwa untuk solusi saya saat ini, saya perlu menangkap-oleh-referensi, itu sebabnya kode berisi logika untuk itu.

Contoh berikut menjelaskan masalah:

#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;
      }
   );
}
David Tth
sumber
6
Karena std::function<void(int)>dapat dibangun bahkan dari lambda yang mengembalikan sesuatu (yang menyebabkan nilai pengembalian diabaikan).
HolyBlackCat
1
Sebagai tambahan, secara eksplisit menentukan tipe kembalinya lambda itu tidak melakukan apa-apa.
Deduplicator

Jawaban:

8

Karena ekspresi lambda ke-2 booldapat dikonversi ke keduanya std::function<void(int)>dan std::function<bool(int)>secara implisit.

std::function memiliki konstruktor yang mengubah:

template< class F >
function( F f );

Konstruktor ini tidak berpartisipasi dalam resolusi kelebihan kecuali f adalah Callable untuk argumen type Args ... dan return type R. (sejak C ++ 14)

Sebagai definisi dari Callable ,

Ekspresi berikut harus valid:

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

di mana INVOKE (f, t1, t2, ..., tN) didefinisikan sebagai static_cast<void>(INVOKE(f, t1, t2, ..., tN))jika R mungkin memenuhi syarat cv void, jika tidak INVOKE (f, t1, t2, ..., tN) , secara implisit dikonversi ke R

Perhatikan bahwa lambda ke-2 bool, untuk std::function<void(int)>, seperti yang ditunjukkan di atas, static_cast<void>(INVOKE(f, t1, t2, ..., tN))adalah ekspresi yang valid (yang dikembalikan boolhanya dikonversi ke void). Kemudian bisa juga dikonversi ke std::function<void(int)>secara implisit dan menyebabkan masalah ambiguitas.

songyuanyao
sumber
6

Anda dapat secara eksplisit static_castlambda ke tipe yang tepat

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;
   }));

Atau simpan lambda ke std::function<bool(int)>jenis yang tepat dan berikan ke fungsi (jika do_some(lmda)harus dipanggil berkali-kali)

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

Atau seperti yang disarankan @MaxLanghof, buat saja std::function<bool(int)>dari lambda saat bepergian

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});
JeJo
sumber
Anda dapat melewati static_castdan hanya membangun std::functionlangsung dari itu. Itu semua yang terjadi selama konversi implisit.
Max Langhof
Maksud saya adalah Anda benar-benar dapat menghapus static_cast<dan bertahan >dan itu akan melakukan hal yang sama tetapi dengan kurang mengetik. Tidak perlu lebih banyak garis atau apa pun. godbolt.org/z/fQTqF4
Max Langhof