Mam kod, który wyszukuje i drukuje dopasowania wzorca przechodzące przez kontener ciągów. Drukowanie odbywa się w funkcji foo, która jest szablonowana
Kod
#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;
}
Podczas kompilacji wystąpił błąd polegający na tym, że dedukcja typów nie powiodła się z powodu niespójności dostarczanych iteratorów, a ich typy okazują się różnorodne.
Błąd kompilacji 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.
Wyjście Clanga :
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)
Czego nie łapię? Czy moje wykorzystanie dedukcji typów szablonów jest niewłaściwe i wydaje się nadużyciem z punktu widzenia standardu? Ani g ++ - 9.2 z listdc ++ 11, ani clang ++ z libc ++ nie są w stanie tego skompilować.
-std=c++17
zi na Clang z-std=c++17
-frelaxed-template-template-args
flagą. W przeciwnym razie wydaje się, że potrzebujesz innego parametru szablonu dla alokatora.Odpowiedzi:
Twój kod powinien działać poprawnie od C ++ 17. (Kompiluje się z gcc10 .)
Argument szablonu szablonu
std::vector
ma dwa parametry szablonu (drugi ma domyślny argumentstd::allocator<T>
), ale parametr szablonu szablonuContainer
ma tylko jeden. Od C ++ 17 ( CWG 150 ) domyślne argumenty szablonu są dozwolone, aby argument szablonu szablonu był zgodny z parametrem szablonu szablonu z mniejszą liczbą parametrów szablonu.W wersjach wcześniejszych niż C ++ 17 można zdefiniować drugi parametr szablonu z domyślnym argumentem parametru szablonu szablonu
Container
, npLub zastosuj pakiet parametrów .
źródło
W niektórych wersjach C ++
Container
nie można dopasowaćstd::vector
, ponieważstd::vector
tak naprawdę nie jesttemplate <typename> class
. To tutajtemplate <typename, typename> class
drugi parametr (typ alokatora) ma domyślny argument szablonu.Chociaż dodanie innego parametru szablonu
typename Alloc
może działać, parametr funkcjiContainer<std::pair<Iterator, Iterator>, Alloc>
może być problemem dla innych typów kontenerów.Ale ponieważ twoja funkcja tak naprawdę nie używa parametru szablonu szablonu
Container
, nie trzeba wymagać tak skomplikowanego odejmowania argumentu szablonu, z wszystkimi problemami i ograniczeniami dedukcji argumentu szablonu szablonu:Nie wymaga
Iterator
to również wywnioskowania jako dokładnie tego samego typu w trzech różnych miejscach. Oznacza to, że poprawne będzie przekazanieX::iterator
asfirst
i kontenera zawierającegoX::const_iterator
lub odwrotnie, a dedukcja argumentów szablonu nadal może się udać.Jedyną niewielką wadą jest to, że jeśli inny szablon korzysta z technik SFINAE w celu ustalenia, czy podpis
foo
jest prawidłowy, deklaracja będzie pasować prawie do wszystkiegofoo(1.0, 2)
. Często nie jest to ważne dla funkcji określonego celu, ale fajnie jest być bardziej restrykcyjnym (lub „przyjaznym dla SFINAE”) przynajmniej dla funkcji ogólnego przeznaczenia. Możemy dodać podstawowe ograniczenie za pomocą czegoś takiego:źródło