Akhir-akhir ini saya menulis fungsi template untuk menyelesaikan beberapa pengulangan kode. Ini terlihat seperti ini:
template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
if (auto sp = ptr.lock())
{
return std::invoke(fun, *sp, args...);
}
else
{
throw std::runtime_error(error.c_str());
}
}
int main() {
auto a = std::make_shared<A>();
call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}
Kode ini berfungsi dengan sangat baik untuk class A
yang terlihat seperti ini:
class A {
public:
void foo(int x) {
}
};
Tetapi gagal mengkompilasi untuk yang seperti ini:
class A {
public:
void foo(const int& x) {
}
};
Mengapa demikian (maksud saya mengapa gagal menyimpulkan jenis) dan bagaimana (jika memungkinkan) saya dapat membuat kode ini berfungsi dengan referensi? Contoh langsung
Args&&...
danstd::forward
?Jawaban:
Masalah Anda adalah Anda memiliki pengurangan konflik
Args
antara:R (T::*fun)(Args...)
Args... args
Saya sarankan untuk memiliki lebih banyak kode generik (tidak ada duplikasi antara
R (T::*fun)(Args...)
danversi const
R (T::*fun)(Args...) const
dan alternatif lain) dengan:sumber
Args
tipe tidak dapat disimpulkan sebagaiconst&
(darifun
deklarasi parameter) dan non-referensi dariargs
deklarasi. Perbaikan sederhana adalah dengan menggunakan dua paket parameter tipe templat terpisah:Sebagai downside, saya bisa membayangkan pesan kesalahan yang sedikit lebih lama jika penggunaannya buruk.
sumber
Args&&... args
Perhatikan bahwa tipe parameter templat
Args
disimpulkan seperticonst int&
pada argumen fungsi ke-3&A::foo
, dan disimpulkan sepertiint
pada parameter fungsi ke-41
. Mereka tidak cocok dan menyebabkan deduksi gagal.Anda dapat mengecualikan parameter ke-4 dari deduksi , mis
HIDUP
PS:
std::type_identity
didukung sejak C ++ 20; tetapi cukup mudah untuk menerapkannya.sumber
Args&&...
, lalu memakaistd::type_identity
parameter ke-3 sepertiR (T::*fun)(std::type_identity_t<Args>...)
. LIVE and LIVE