Saya memiliki kode yang menemukan dan mencetak kecocokan suatu pola ketika melewati wadah string. Pencetakan dilakukan dalam fungsi foo yang templated
Kode
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>
#include <tuple>
#include <utility>
template<typename Iterator, template<typename> class Container>
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)
{
for (auto const &finding : findings)
{
std::cout << "pos = " << std::distance(first, finding.first) << " ";
std::copy(finding.first, finding.second, std::ostream_iterator<char>(std::cout));
std::cout << '\n';
}
}
int main()
{
std::vector<std::string> strs = { "hello, world", "world my world", "world, it is me" };
std::string const pattern = "world";
for (auto const &str : strs)
{
std::vector<std::pair<std::string::const_iterator, std::string::const_iterator>> findings;
for (std::string::const_iterator match_start = str.cbegin(), match_end;
match_start != str.cend();
match_start = match_end)
{
match_start = std::search(match_start, str.cend(), pattern.cbegin(), pattern.cend());
if (match_start != match_end)
findings.push_back({match_start, match_start + pattern.size()});
}
foo(str.cbegin(), findings);
}
return 0;
}
Ketika mengkompilasi saya punya kesalahan bahwa deduksi tipe telah gagal karena inkonsistensi iterator yang disediakan, tipenya ternyata beragam.
Kesalahan kompilasi GCC :
prog.cpp:35:9: error: no matching function for call to 'foo'
foo(str.cbegin(), findings);
^~~
prog.cpp:10:6: note: candidate template ignored: substitution failure [with Iterator = __gnu_cxx::__normal_iterator<const char *, std::__cxx11::basic_string<char> >]: template template argument has different template parameters than its corresponding template template parameter
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)
^
1 error generated.
Output dentang :
main.cpp:34:9: error: no matching function for call to 'foo'
foo(str.cbegin(), findings);
^~~
main.cpp:9:6: note: candidate template ignored: substitution failure [with Iterator = std::__1::__wrap_iter<const char *>]: template template argument has different template parameters than its corresponding template template parameter
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)
Apa yang tidak saya tangkap? Apakah pemanfaatan jenis template template saya salah dan muncul penyalahgunaan dari sudut pandang standar? Baik g ++ - 9.2 dengan listdc ++ 11 atau clang ++ dengan libc ++ dapat mengkompilasi ini.
-std=c++17
dan pada Dentang dengan-std=c++17
-frelaxed-template-template-args
bendera. Kalau tidak , Anda perlu parameter templat lain untuk pengalokasi.Jawaban:
Kode Anda harus berfungsi dengan baik sejak C ++ 17. (Ini dikompilasi dengan gcc10 .)
Argumen template template
std::vector
memiliki dua parameter template (yang ke-2 memiliki argumen defaultstd::allocator<T>
), tetapi parameter template templateContainer
hanya memiliki satu. Karena C ++ 17 ( CWG 150 ), argumen template default diizinkan untuk argumen template template untuk mencocokkan parameter template template dengan lebih sedikit parameter template.Sebelum C ++ 17, Anda dapat menentukan parameter template 2 dengan argumen default untuk parameter template template
Container
, misalnyaAtau terapkan paket parameter .
sumber
Di beberapa versi C ++,
Container
tidak bisa cocokstd::vector
, karenastd::vector
sebenarnya bukan atemplate <typename> class
. Itu adalahtemplate <typename, typename> class
tempat parameter kedua (tipe pengalokasi) memiliki argumen templat default.Meskipun bisa berfungsi untuk menambahkan parameter templat lain
typename Alloc
membuat parameter fungsiContainer<std::pair<Iterator, Iterator>, Alloc>
, itu bisa menjadi masalah untuk jenis wadah lainnya.Tetapi karena fungsi Anda tidak benar-benar menggunakan parameter templat templat
Container
, Anda tidak perlu memerlukan deduksi argumen templat yang rumit, dengan semua gotcha dan batasan menyimpulkan argumen templat templat:Ini juga tidak perlu
Iterator
disimpulkan sebagai tipe yang sama persis di tiga tempat berbeda. Berarti itu akan berlaku untuk lulusX::iterator
sebagaifirst
dan wadah berisiX::const_iterator
atau sebaliknya, dan pengurangan argumen templat masih bisa berhasil.Satu kelemahannya adalah jika templat lain menggunakan teknik SFINAE untuk mencoba menentukan apakah tanda tangan
foo
valid, deklarasi itu akan cocok dengan hampir semua hal, sepertifoo(1.0, 2)
. Ini sering tidak penting untuk fungsi tujuan tertentu, tapi senang menjadi lebih membatasi (atau "ramah SFINAE") setidaknya untuk fungsi tujuan umum. Kita dapat menambahkan batasan dasar dengan sesuatu seperti:sumber