文字列のコンテナを調べて、パターンの一致を見つけて出力するコードがあります。テンプレート化された関数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's出力:
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 with listdc ++ 11もclang ++ with libc ++もこれをコンパイルできません。
C++の一部のバージョンでは、_std::vector
_は実際には_std::vector
_ではないため、Container
は_template <typename> class
_と一致できません。これは_template <typename, typename> class
_で、2番目のパラメータ(アロケータタイプ)にはデフォルトのテンプレート引数があります。
別のテンプレートパラメータ_typename Alloc
_を追加して関数パラメータを_Container<std::pair<Iterator, Iterator>, Alloc>
_にすることもできますが、それは他のコンテナタイプでは問題になる可能性があります。
ただし、関数は実際にはテンプレートテンプレートパラメーターContainer
を使用しないため、テンプレートテンプレート引数を推測する上でのすべての問題点と制限があるため、このような複雑なテンプレート引数を推測する必要はありません。
_template<typename Iterator, class Container>
void foo(Iterator first, Container const &findings);
_
これは、3つの異なる場所でまったく同じ型としてIterator
を推定する必要もありません。つまり、_X::iterator
_をfirst
として渡し、_X::const_iterator
_を含むコンテナ、またはその逆を渡すことが有効であり、テンプレート引数の推定は引き続き成功する可能性があります。
わずかな欠点の1つは、別のテンプレートが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>>>;
_