У меня есть код, который находит и распечатывает совпадения шаблона, как проходя через контейнер строк. Печать выполняется в функции foo, которая является шаблонной.
Код
#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;
}
При компиляции у меня возникает ошибка, что вывод типов завершился неудачей из-за несогласованности предоставляемых итераторов, их типы оказываются разнообразными.
Ошибка компиляции 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.
Выход 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)
Что я не ловлю? Является ли мое использование вывода шаблонных шаблонов неправильным и является ли это злоупотреблением с точки зрения стандарта? Ни g ++ - 9.2 с listdc ++ 11, ни clang ++ с libc ++ не могут скомпилировать это.
-std=c++17
и на Clang с-std=c++17
-frelaxed-template-template-args
флагом. В противном случае кажется, что вам нужен другой параметр шаблона для распределителя.Ответы:
Ваш код должен работать нормально, начиная с C ++ 17. (Он компилируется с gcc10 .)
Аргумент шаблона шаблона
std::vector
имеет два параметра шаблона (2-й имеет аргумент по умолчаниюstd::allocator<T>
), но параметр шаблона шаблонаContainer
имеет только один. Начиная с C ++ 17 ( CWG 150 ), аргументы шаблона по умолчанию разрешены для аргумента шаблона шаблона, чтобы соответствовать параметру шаблона шаблона с меньшим количеством параметров шаблона.До C ++ 17 вы можете определить 2-й параметр шаблона с аргументом по умолчанию для параметра шаблона шаблона
Container
, напримерИли примените пакет параметров .
источник
В некоторых версиях C ++
Container
не может совпадатьstd::vector
, потому что наstd::vector
самом деле не являетсяtemplate <typename> class
. Этоtemplate <typename, typename> class
где второй параметр (тип распределителя) имеет аргумент шаблона по умолчанию.Хотя это может сработать, чтобы добавить другой параметр шаблона,
typename Alloc
сделать параметр функцииContainer<std::pair<Iterator, Iterator>, Alloc>
, это может быть проблемой для других типов контейнеров.Но так как ваша функция на самом деле не использует параметр шаблона шаблона
Container
, нет необходимости требовать такого сложного вывода аргумента шаблона со всеми хитростями и ограничениями вывода аргумента шаблона шаблона:Это также не требует
Iterator
вывода как один и тот же тип в трех разных местах. Это означает, что будет допустимо передатьX::iterator
asfirst
и контейнер, содержащийX::const_iterator
или наоборот, и вывод аргумента шаблона все еще может быть успешным.Один небольшой недостаток заключается в том, что если другой шаблон использует методы SFINAE, чтобы попытаться определить, действительна ли подпись
foo
, это объявление будет соответствовать почти чему-либо, напримерfoo(1.0, 2)
. Это часто не важно для функции специального назначения, но приятно быть более ограничительным (или «дружественным по отношению к SFINAE»), по крайней мере, для функций общего назначения. Мы могли бы добавить основное ограничение, например:источник