Saya tahu bahwa kode berikut tidak akan dikompilasi.
void baz(int i) { }
void baz() { }
class Bar
{
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = baz) : bazFn(fun){}
};
int main(int argc, char **argv)
{
Bar b;
return 0;
}
Karena std::function
dikatakan tidak mempertimbangkan resolusi overload, seperti yang saya baca posting lain ini .
Saya tidak sepenuhnya memahami keterbatasan teknis yang memaksa solusi semacam ini.
Saya membaca tentang fase - fase terjemahan dan template pada cppreference, tetapi saya tidak dapat memikirkan alasan apa pun yang saya tidak dapat menemukan contoh tandingannya. Dijelaskan kepada orang awam (masih baru di C ++), apa dan selama tahap terjemahan mana yang membuat gagal untuk dikompilasi?
c++
templates
translation
std-function
TuRtoise
sumber
sumber
Jawaban:
Ini tidak benar-benar ada hubungannya dengan "fase terjemahan". Ini murni tentang konstruktor
std::function
.Lihat,
std::function<R(Args)>
tidak mengharuskan fungsi yang diberikan persis dari jenisnyaR(Args)
. Secara khusus, itu tidak mengharuskan diberi fungsi pointer. Ia dapat mengambil tipe apa pun yang dapat dipanggil (pointer fungsi anggota, beberapa objek yang memiliki kelebihanoperator()
) asalkan tidak dapat dimasuki seolah-olah mengambilArgs
parameter dan mengembalikan sesuatu yang dapat dikonversi keR
(atau jikaR
inivoid
, dapat kembali apa-apa).Untuk melakukan itu, konstruktor yang sesuai
std::function
harus berupa templat :template<typename F> function(F f);
. Artinya, dapat mengambil jenis fungsi apa pun (tunduk pada pembatasan di atas).Ekspresi
baz
mewakili set overload. Jika Anda menggunakan ungkapan itu untuk memanggil set overload, itu tidak masalah. Jika Anda menggunakan ekspresi itu sebagai parameter ke fungsi yang mengambil penunjuk fungsi tertentu, C ++ dapat mengurangi overload yang ditetapkan ke satu panggilan, sehingga membuatnya baik-baik saja.Namun, begitu suatu fungsi adalah templat, dan Anda menggunakan deduksi argumen templat untuk mengetahui parameter apa itu, C ++ tidak lagi memiliki kemampuan untuk menentukan apa kelebihan beban yang benar dalam kumpulan kelebihan tersebut. Jadi, Anda harus menentukannya secara langsung.
sumber
function
templat kelas tidak relevan . Yang penting adalah parameter template pada konstruktor yang Anda panggil. Yang mana sajatypename F
: alias, tipe apa saja.Resolusi kelebihan terjadi hanya ketika (a) Anda memanggil nama fungsi / operator, atau (b) melemparkannya ke pointer (berfungsi atau fungsi anggota) dengan tanda tangan eksplisit.
Tidak ada yang terjadi di sini.
std::function
mengambil objek apa pun itu kompatibel dengan tanda tangannya. Tidak memerlukan pointer fungsi secara khusus. (lambda bukan fungsi std, dan fungsi std bukan lambda)Sekarang dalam varian fungsi homebrew saya, untuk tanda tangan
R(Args...)
saya juga menerimaR(*)(Args...)
argumen (pencocokan persis) untuk alasan ini. Tetapi itu berarti meningkatkan tanda tangan "kecocokan tepat" di atas tanda tangan "kompatibel".Masalah intinya adalah bahwa set overload bukan objek C ++. Anda dapat memberi nama kumpulan kelebihan, tetapi Anda tidak dapat memutarnya "secara bawaan".
Sekarang, Anda dapat membuat set fungsi pseudo-overload seperti ini:
ini menciptakan objek C ++ tunggal yang dapat melakukan resolusi kelebihan pada nama fungsi.
Memperluas makro, kita mendapatkan:
yang menjengkelkan untuk menulis. Versi yang lebih sederhana, hanya sedikit kurang bermanfaat, ada di sini:
kami memiliki lambda yang membutuhkan sejumlah argumen, kemudian menyempurnakannya
baz
.Kemudian:
bekerja. Kami menunda resolusi kelebihan ke dalam lambda yang kami simpan
fun
, alih-alih lewatfun
set kelebihan beban secara langsung (yang tidak dapat diselesaikan).Setidaknya ada satu proposal untuk mendefinisikan operasi dalam bahasa C ++ yang mengubah nama fungsi menjadi objek set yang kelebihan. Sampai proposal standar tersebut ada dalam standar,
OVERLOADS_OF
makro berguna.Anda bisa melangkah lebih jauh, dan mendukung cast-to-compatible-function-pointer.
tapi itu mulai tumpul.
Contoh langsung .
sumber
Masalahnya di sini adalah tidak ada yang memberitahu kompiler bagaimana melakukan fungsi untuk membusuk pointer. Jika Anda memiliki
Kemudian kode akan berfungsi karena sekarang kompiler mengetahui fungsi yang Anda inginkan karena ada jenis konkret yang Anda tetapkan.
Ketika Anda menggunakan
std::function
Anda menyebutnya pembangun objek fungsi yang memiliki bentukdan karena itu adalah templat, ia perlu menyimpulkan jenis objek yang dilewatkan. karena
baz
merupakan fungsi kelebihan tidak ada tipe tunggal yang dapat dideduksi sehingga deduksi template gagal dan Anda mendapatkan kesalahan. Anda harus menggunakanuntuk mendapatkan kekuatan satu jenis dan memungkinkan deduksi.
sumber
Pada titik ini kompiler memutuskan overload mana yang akan masuk ke
std::function
konstruktor yang diketahuinya adalah bahwastd::function
konstruktor tersebut dibuat untuk mengambil tipe apa pun. Itu tidak memiliki kemampuan untuk mencoba kedua kelebihan dan menemukan bahwa yang pertama tidak dikompilasi tetapi yang kedua tidak.Cara untuk mengatasinya adalah dengan secara eksplisit memberi tahu kompiler mana yang kelebihan Anda inginkan dengan
static_cast
:sumber