Ich habe Code, der Übereinstimmungen eines Musters findet und ausdruckt, indem er über den Container mit Zeichenfolgen geht. Der Druck wird in der Funktion ausgeführt foo , die als Templat wird
Der Code
#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;
}
Beim Kompilieren ist der Fehler aufgetreten, dass der Typabzug aufgrund der Inkonsistenz der bereitgestellten Iteratoren fehlgeschlagen ist. Ihre Typen sind unterschiedlich.
GCC- Kompilierungsfehler:
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.
Clangs Ausgabe:
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)
Was fange ich nicht? Ist meine Verwendung des Abzugs von Vorlagenvorlagentypen falsch und erscheint aus Sicht des Standards als Missbrauch? Weder g ++ - 9.2 mit listdc ++ 11 noch clang ++ mit libc ++ können dies kompilieren.
-std=c++17
und auf Clang mit-std=c++17
-frelaxed-template-template-args
Flagge. Andernfalls benötigen Sie anscheinend einen anderen Vorlagenparameter für den Allokator.Antworten:
Ihr Code sollte seit C ++ 17 einwandfrei funktionieren. (Es wird mit gcc10 kompiliert .)
Das Vorlagenvorlagenargument
std::vector
enthält zwei Vorlagenparameter (der zweite hat das Standardargumentstd::allocator<T>
), der VorlagenvorlagenparameterContainer
jedoch nur einen. Seit C ++ 17 ( CWG 150 ) sind die Standardvorlagenargumente für Vorlagenvorlagenargumente zulässig, um Vorlagenvorlagenparameter mit weniger Vorlagenparametern abzugleichen.Vor C ++ 17 können Sie den zweiten Vorlagenparameter mit dem Standardargument für den Vorlagenvorlagenparameter definieren
Container
, zOder wenden Sie das Parameterpaket an .
quelle
In einigen Versionen von C ++
Container
kann nicht übereinstimmenstd::vector
, dastd::vector
es sich eigentlich nicht um eine handelttemplate <typename> class
. Hier hattemplate <typename, typename> class
der zweite Parameter (der Allokatortyp) ein Standardvorlagenargument.Obwohl es funktionieren könnte, einen weiteren Vorlagenparameter hinzuzufügen,
typename Alloc
machen Sie den FunktionsparameterContainer<std::pair<Iterator, Iterator>, Alloc>
, dies könnte ein Problem für andere Containertypen sein.Da Ihre Funktion den Vorlagenvorlagenparameter jedoch nicht tatsächlich verwendet, muss kein
Container
so komplizierter Abzug von Vorlagenargumenten erforderlich sein, mit allen Fallstricken und Einschränkungen beim Ableiten eines Vorlagenvorlagenarguments:Dies muss auch nicht
Iterator
an drei verschiedenen Stellen als exakt der gleiche Typ abgeleitet werden. Das heißt, es ist gültig, einX::iterator
asfirst
und einen Container zu übergeben, der enthältX::const_iterator
oder umgekehrt, und der Abzug von Vorlagenargumenten könnte weiterhin erfolgreich sein.Der einzige kleine Nachteil ist, dass, wenn eine andere Vorlage SFINAE-Techniken verwendet, um festzustellen, ob eine Signatur von
foo
gültig ist, diese Deklaration mit fast allem übereinstimmt, wie zfoo(1.0, 2)
. Dies ist oft nicht wichtig für eine bestimmte Funktion, aber es ist schön, zumindest für allgemeine Funktionen restriktiver (oder "SFINAE-freundlich") zu sein. Wir könnten eine grundlegende Einschränkung hinzufügen mit etwas wie:quelle