Tengo un código que encuentra e imprime las coincidencias de un patrón que va sobre el contenedor de cadenas. La impresión se realiza en la función foo que tiene la plantilla
El código
#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;
}
Al compilar, tengo un error de que la deducción de tipos ha fallado debido a la inconsistencia de los iteradores proporcionados, sus tipos resultan ser diversos.
Error de compilación de 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.
Salida 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é no estoy captando? ¿Mi utilización de la deducción de tipos de plantilla de plantilla es incorrecta y parece un abuso desde el punto de vista del estándar? Ni g ++ - 9.2 con listdc ++ 11 ni clang ++ con libc ++ pueden compilar esto.
-std=c++17
y en Clang con-std=c++17
-frelaxed-template-template-args
bandera. De lo contrario , parece que necesita otro parámetro de plantilla para el asignador.Respuestas:
Su código debería funcionar bien desde C ++ 17. (Se compila con gcc10 .)
El argumento de plantilla de plantilla
std::vector
tiene dos parámetros de plantilla (el segundo tiene un argumento predeterminadostd::allocator<T>
), pero el parámetro de plantilla de plantillaContainer
tiene solo uno. Desde C ++ 17 ( CWG 150 ), los argumentos de plantilla predeterminados están permitidos para que el argumento de plantilla coincida con el parámetro de plantilla de plantilla con menos parámetros de plantilla.Antes de C ++ 17, puede definir el segundo parámetro de plantilla con un argumento predeterminado para el parámetro de plantilla de plantilla
Container
, p. Ej.O aplique el paquete de parámetros .
fuente
En algunas versiones de C ++,
Container
no puede coincidirstd::vector
, porque enstd::vector
realidad no es untemplate <typename> class
. Es un lugartemplate <typename, typename> class
donde el segundo parámetro (el tipo de asignador) tiene un argumento de plantilla predeterminado.Aunque podría funcionar agregar otro parámetro de plantilla para
typename Alloc
hacer que el parámetro de funciónContainer<std::pair<Iterator, Iterator>, Alloc>
, eso podría ser un problema para otros tipos de contenedor.Pero dado que su función en realidad no utiliza el parámetro de plantilla de plantilla
Container
, no es necesario exigir una deducción de argumento de plantilla tan complicada, con todas las trampas y limitaciones de deducir un argumento de plantilla de plantilla:Esto tampoco requiere
Iterator
ser deducido exactamente como el mismo tipo en tres lugares diferentes. Lo que significa que será válido pasar unX::iterator
asfirst
y un contenedor que contengaX::const_iterator
o viceversa, y la deducción de argumentos de plantilla aún podría tener éxito.El único inconveniente es que si otra plantilla usa técnicas SFINAE para tratar de determinar si una firma
foo
es válida, esa declaración coincidiría con casi cualquier cosa, comofoo(1.0, 2)
. Esto a menudo no es importante para una función de propósito específico, pero es bueno ser más restrictivo (o "compatible con SFINAE") al menos para funciones de propósito general. Podríamos agregar una restricción básica con algo como:fuente