我有一些代码,可以在字符串容器中查找并打印出模式匹配项。在模板化的功能 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.
C的输出:
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)
我没有抓住什么?我对模板模板类型的使用是否推论错误,并且从标准的角度来看似乎是一种滥用?带有 listdc ++ 11 的 g ++-9.2 和带有 libc ++ 的 clang ++ 都不能编译此代码。
答案 0 :(得分:11)
自C ++ 17起,您的代码应该可以正常工作。 (它使用gcc10进行编译。)
模板模板参数std::vector
具有两个模板参数(第二个模板参数具有默认参数std::allocator<T>
),但是模板模板参数Container
只有一个。从C ++ 17(CWG 150开始,template template argument允许使用默认模板参数来匹配模板模板参数较少的模板参数。
template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<template<class> class P> class X { /* ... */ }; X<A> xa; // OK X<B> xb; // OK in C++17 after CWG 150 // Error earlier: not an exact match
在C ++ 17之前,您可以使用模板模板参数Container
的默认参数定义第二个模板参数,例如
template<typename Iterator, template<typename T, typename Alloc=std::allocator<T>> class Container>
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)
或应用parameter pack。
template<typename Iterator, template<typename...> class Container>
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)
答案 1 :(得分:1)
在某些C ++版本中,Container
与std::vector
不匹配,因为std::vector
实际上不是template <typename> class
。这是template <typename, typename> class
,其中第二个参数(分配器类型)具有默认模板参数。
尽管添加另一个模板参数typename Alloc
使函数参数Container<std::pair<Iterator, Iterator>, Alloc>
可行,但这可能是其他容器类型的问题。
但是,由于您的函数实际上并未使用模板模板参数Container
,因此无需进行如此复杂的模板参数推导,就需要推导模板模板参数的所有陷阱和局限性:
template<typename Iterator, class Container>
void foo(Iterator first, Container const &findings);
这也不要求在三个不同的地方将Iterator
推导出为完全相同的类型。这意味着将X::iterator
传递为first
和包含X::const_iterator
的容器是有效的,反之亦然,并且模板参数推导仍然可以成功。
一个小缺点是,如果另一个模板使用SFINAE技术来尝试确定foo
的签名是否有效,则该声明将几乎匹配任何内容,例如foo(1.0, 2)
。这对于特定用途的功能通常并不重要,但至少对于通用功能更严格(或“对SFINAE友好”),这很好。我们可以添加类似以下内容的基本限制:
// Require Container is container-like (including raw array or std::initializer_list)
// and its values have members first and second of the same type,
// which can be compared for equality with Iterator.
template <typename Iterator, class Container>
auto foo(Iterator first, Container const &findings)
-> std::void_t<decltype(first == std::begin(findings)->first),
std::enable_if_t<std::is_same_v<std::begin(findings)->first,
std::begin(findings)->second>>>;