J'ai du code qui trouve et imprime les correspondances d'un modèle en passant par le conteneur de chaînes. L'impression est effectuée dans la fonction foo qui est modélisée
Le 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;
}
Lors de la compilation, j'ai une erreur indiquant que la déduction des types a échoué en raison de l'incohérence des itérateurs fournis, leurs types se révèlent divers.
Erreur de compilation 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.
Sortie de Clang :
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)
Qu'est-ce que je n'attrape pas? Mon utilisation de la déduction des types de modèle de modèle est-elle incorrecte et semble-t-elle un abus du point de vue de la norme? Ni g ++ - 9.2 avec listdc ++ 11 ni clang ++ avec libc ++ ne peuvent le compiler.
-std=c++17
et sur Clang avec-std=c++17
-frelaxed-template-template-args
drapeau. Sinon, il semble que vous ayez besoin d'un autre paramètre de modèle pour l'allocateur.Réponses:
Votre code devrait fonctionner correctement depuis C ++ 17. (Il compile avec gcc10 .)
L'argument de modèle de modèle
std::vector
a deux paramètres de modèle (le deuxième a un argument par défautstd::allocator<T>
), mais le paramètre de modèle de modèleContainer
n'en a qu'un. Depuis C ++ 17 ( CWG 150 ), les arguments de modèle par défaut sont autorisés pour que l' argument de modèle de modèle corresponde au paramètre de modèle de modèle avec moins de paramètres de modèle.Avant C ++ 17, vous pouvez définir le 2ème paramètre de modèle avec un argument par défaut pour le paramètre de modèle de modèle
Container
, par exempleOu appliquez le pack de paramètres .
la source
Dans certaines versions de C ++,
Container
ne peut pas correspondrestd::vector
, car cestd::vector
n'est pas réellement untemplate <typename> class
. C'est untemplate <typename, typename> class
où le deuxième paramètre (le type d'allocateur) a un argument de modèle par défaut.Bien qu'il puisse fonctionner pour ajouter un autre paramètre de modèle
typename Alloc
rendre le paramètre de fonctionContainer<std::pair<Iterator, Iterator>, Alloc>
, cela pourrait être un problème pour d'autres types de conteneurs.Mais puisque votre fonction n'utilise pas réellement le paramètre de modèle de modèle
Container
, il n'est pas nécessaire d'exiger une déduction d'argument de modèle aussi compliquée, avec tous les pièges et les limitations de déduire un argument de modèle de modèle:Cela ne nécessite pas non plus
Iterator
d'être déduit comme le même type exact à trois endroits différents. Cela signifie qu'il sera valide de passer unX::iterator
asfirst
et un conteneur contenantX::const_iterator
ou vice versa, et la déduction d'argument de modèle pourrait toujours réussir.Le seul léger inconvénient est que si un autre modèle utilise des techniques SFINAE pour essayer de déterminer si une signature de
foo
est valide, cette déclaration correspondrait à presque n'importe quoi, commefoo(1.0, 2)
. Ce n'est souvent pas important pour une fonction spécifique, mais il est agréable d'être plus restrictif (ou "compatible SFINAE") au moins pour les fonctions générales. Nous pourrions ajouter une restriction de base avec quelque chose comme:la source