Eu tenho um código que localiza e imprime correspondências de um padrão como passando por cima do contêiner de strings. A impressão é realizada na função foo que é templated
O 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;
}
Ao compilar, recebi um erro de que a dedução de tipos falhou devido à inconsistência de iteradores sendo fornecidos, seus tipos acabam sendo diversos.
Erro de compilação do 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.
Saída 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)
O que eu não estou pegando? Minha utilização da dedução de tipos de modelo está errada e parece um abuso do ponto de vista do padrão? Nem o g ++ - 9.2 com listdc ++ 11 nem o clang ++ com libc ++ são capazes de compilar isso.
-std=c++17
e no Clang com-std=c++17
-frelaxed-template-template-args
flag. Caso contrário , parece que você precisa de outro parâmetro de modelo para o alocador.Respostas:
Seu código deve funcionar bem desde o C ++ 17. (Compila com gcc10 .)
O argumento do modelo
std::vector
tem dois parâmetros de modelo (o segundo tem argumento padrãostd::allocator<T>
), mas o parâmetro do modeloContainer
tem apenas um. Desde C ++ 17 ( CWG 150 ), os argumentos do modelo padrão são permitidos para que o argumento do modelo corresponda ao parâmetro do modelo com menos parâmetros do modelo.Antes do C ++ 17, você pode definir o segundo parâmetro do modelo com argumento padrão para o parâmetro do modelo
Container
, por exemploOu aplique o pacote de parâmetros .
fonte
Em algumas versões do C ++,
Container
não pode corresponderstd::vector
, porquestd::vector
na verdade não é umtemplate <typename> class
. Étemplate <typename, typename> class
onde o segundo parâmetro (o tipo de alocador) tem um argumento de modelo padrão.Embora possa funcionar para adicionar outro parâmetro de modelo ao parâmetro de
typename Alloc
funçãoContainer<std::pair<Iterator, Iterator>, Alloc>
, isso pode ser um problema para outros tipos de contêiner.Mas como sua função não usa o parâmetro de modelo de modelo
Container
, não há necessidade de exigir uma dedução de argumento de modelo tão complicada, com todas as vantagens e limitações de deduzir um argumento de modelo de modelo:Isso também não precisa
Iterator
ser deduzido como exatamente o mesmo tipo em três locais diferentes. Significando que será válido passar umX::iterator
asfirst
e um contêiner contendoX::const_iterator
ou vice-versa, e a dedução do argumento do modelo ainda poderá ser bem-sucedida.A única desvantagem é que, se outro modelo usar técnicas SFINAE para tentar determinar se uma assinatura
foo
é válida, essa declaração corresponderá a quase qualquer coisa, comofoo(1.0, 2)
. Isso geralmente não é importante para uma função de finalidade específica, mas é bom ser mais restritivo (ou "compatível com SFINAE") pelo menos para funções de finalidade geral. Poderíamos adicionar uma restrição básica com algo como:fonte