Saya ingin meneruskan fungsi yang kelebihan beban ke std::for_each()
algoritme. Sebagai contoh,
class A {
void f(char c);
void f(int i);
void scan(const std::string& s) {
std::for_each(s.begin(), s.end(), f);
}
};
Saya mengharapkan kompiler untuk menyelesaikan f()
dengan tipe iterator. Rupanya, itu (GCC 4.1.2) tidak melakukannya. Jadi, bagaimana saya bisa menentukan mana yang f()
saya inginkan?
Jawaban:
Anda dapat menggunakan
static_cast<>()
untuk menentukan mana yangf
akan digunakan sesuai dengan tanda tangan fungsi yang tersirat oleh tipe penunjuk fungsi:// Uses the void f(char c); overload std::for_each(s.begin(), s.end(), static_cast<void (*)(char)>(&f)); // Uses the void f(int i); overload std::for_each(s.begin(), s.end(), static_cast<void (*)(int)>(&f));
Atau, Anda juga bisa melakukan ini:
// The compiler will figure out which f to use according to // the function pointer declaration. void (*fpc)(char) = &f; std::for_each(s.begin(), s.end(), fpc); // Uses the void f(char c); overload void (*fpi)(int) = &f; std::for_each(s.begin(), s.end(), fpi); // Uses the void f(int i); overload
Jika
f
merupakan fungsi anggota, maka Anda perlu menggunakanmem_fun
, atau untuk kasus Anda, gunakan solusi yang disajikan dalam artikel Dr. Dobb ini .sumber
f()
adalah anggota kelas (lihat contoh yang diedit di atas)reinterpret_cast
. Paling sering saya melihat gips gaya C digunakan untuk ini. Aturan saya hanya bahwa cast pada pointer fungsi berbahaya dan tidak perlu (seperti yang ditunjukkan cuplikan kode kedua, ada konversi implisit).std::for_each(s.begin(), s.end(), static_cast<void (A::*)(char)>(&A::f));
Lambdas untuk menyelamatkan! (catatan: C ++ 11 diperlukan)
std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });
Atau menggunakan jenis deklarasi untuk parameter lambda:
std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); });
Dengan lambda polimorfik (C ++ 14):
std::for_each(s.begin(), s.end(), [&](auto a){ return f(a); });
Atau uraikan dengan menghapus kelebihan beban (hanya berfungsi untuk fungsi gratis):
void f_c(char i) { return f(i); } void scan(const std::string& s) { std::for_each(s.begin(), s.end(), f_c); }
sumber
mem_fn
danbind
, yang, BTW. Juga C ++ 11). Juga, jika kita ingin benar-benar bertele-tele[&](char a){ return f(a); }
adalah 28 karakter, danstatic_cast<void (A::*)(char)>(&f)
35 karakter.Mengapa tidak berhasil
Akan lebih bagus jika itu masalahnya! Namun,
for_each
adalah template fungsi, dideklarasikan sebagai:template <class InputIterator, class UnaryFunction> UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );
Pemotongan template perlu memilih jenis untuk
UnaryFunction
pada saat panggilan. Tetapif
tidak memiliki tipe tertentu - ini adalah fungsi yang kelebihan beban, ada banyakf
yang masing-masing memiliki tipe yang berbeda. Saat ini tidak ada cara untukfor_each
membantu proses pemotongan template dengan menyatakan yangf
diinginkan, jadi pemotongan template gagal. Agar pemotongan template berhasil, Anda perlu melakukan lebih banyak pekerjaan di situs panggilan.Solusi umum untuk memperbaikinya
Melompat ke sini beberapa tahun dan C ++ 14 kemudian. Daripada menggunakan a
static_cast
(yang akan memungkinkan pemotongan template berhasil dengan "memperbaiki" yangf
ingin kita gunakan, tetapi mengharuskan Anda untuk melakukan resolusi berlebih secara manual untuk "memperbaiki" yang benar), kami ingin membuat kompilator berfungsi untuk kita. Kami inginf
mengajukan beberapa argumen. Dengan cara yang paling umum, itu:[&](auto&&... args) -> decltype(auto) { return f(std::forward<decltype(args)>(args)...); }
Itu banyak untuk diketik, tapi masalah semacam ini sering muncul menjengkelkan, jadi kita bisa membungkusnya dalam makro (menghela napas):
#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(func(std::forward<decltype(args)>(args)...)) { return func(std::forward<decltype(args)>(args)...); }
dan kemudian gunakan saja:
void scan(const std::string& s) { std::for_each(s.begin(), s.end(), AS_LAMBDA(f)); }
Ini akan melakukan apa yang Anda inginkan dari kompilator - melakukan resolusi overload pada namanya
f
dan melakukan hal yang benar. Ini akan berfungsi terlepas dari apakahf
fungsi bebas atau fungsi anggota.sumber
Bukan untuk menjawab pertanyaan Anda, tetapi saya satu-satunya yang menemukan
for ( int i = 0; i < s.size(); i++ ) { f( s[i] ); }
lebih sederhana dan lebih pendek dari
for_each
alternatif yang disarankan oleh in silico dalam kasus ini?sumber
Masalahnya di sini tampaknya bukan pada resolusi yang berlebihan tetapi pada kenyataannya pengurangan parameter template . Meskipun jawaban yang sangat baik dari @In silico akan menyelesaikan masalah kelebihan beban yang ambigu secara umum, tampaknya perbaikan terbaik saat berhadapan dengan
std::for_each
(atau serupa) adalah dengan secara eksplisit menentukan parameter templatnya :// Simplified to use free functions instead of class members. #include <algorithm> #include <iostream> #include <string> void f( char c ) { std::cout << c << std::endl; } void f( int i ) { std::cout << i << std::endl; } void scan( std::string const& s ) { // The problem: // error C2914: 'std::for_each' : cannot deduce template argument as function argument is ambiguous // std::for_each( s.begin(), s.end(), f ); // Excellent solution from @In silico (see other answer): // Declare a pointer of the desired type; overload resolution occurs at time of assignment void (*fpc)(char) = f; std::for_each( s.begin(), s.end(), fpc ); void (*fpi)(int) = f; std::for_each( s.begin(), s.end(), fpi ); // Explicit specification (first attempt): // Specify template parameters to std::for_each std::for_each< std::string::const_iterator, void(*)(char) >( s.begin(), s.end(), f ); std::for_each< std::string::const_iterator, void(*)(int) >( s.begin(), s.end(), f ); // Explicit specification (improved): // Let the first template parameter be derived; specify only the function type std::for_each< decltype( s.begin() ), void(*)(char) >( s.begin(), s.end(), f ); std::for_each< decltype( s.begin() ), void(*)(int) >( s.begin(), s.end(), f ); } void main() { scan( "Test" ); }
sumber
Jika Anda tidak keberatan menggunakan C ++ 11, berikut adalah penolong pintar yang mirip dengan (tapi tidak seburuk) cast statis:
template<class... Args, class T, class R> auto resolve(R (T::*m)(Args...)) -> decltype(m) { return m; } template<class T, class R> auto resolve(R (T::*m)(void)) -> decltype(m) { return m; }
(Berfungsi untuk fungsi anggota; harus jelas cara memodifikasinya agar berfungsi untuk fungsi yang berdiri sendiri, dan Anda harus dapat menyediakan kedua versi dan kompilator akan memilih yang tepat untuk Anda.)
Dengan terima kasih kepada Miro Knejp karena telah menyarankan: lihat juga https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J .
sumber
R
, itu tidak disimpulkan. Itu juga tidak disebutkan dalam jawaban ini.R
, saya menyediakanArgs
.R
danT
disimpulkan. Memang benar jawabannya bisa diperbaiki. (Tidak adaT
dalam contoh saya, karena ini bukan pointer-ke-anggota, karena itu tidak akan berhasilstd::for_each
.)