C ++ 템플릿 템플릿 인수 유형 공제


10

문자열 컨테이너를 통해 패턴 일치를 찾아 인쇄하는 코드가 있습니다. 템플릿 화 된 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.2listdc ++ 11연타 ++의 libc ++은 이 컴파일 할 수 있습니다.


1
플래그가있는 -std=c++17CCC와 Clang 에서 작동합니다 -std=c++17-frelaxed-template-template-args. 그렇지 않으면 할당 자에 대한 다른 템플릿 매개 변수가 필요한 것 같습니다 .
HolyBlackCat

@HolyBlackCat, 정말로, 감사합니다
dannftk

답변:


10

C ++ 17부터 코드가 제대로 작동합니다. (와 컴파일 gcc10으로 .)

템플리트 템플리트 인수 std::vector에는 두 개의 템플리트 매개 변수가 있으며 (두 번째 템플리트에는 기본 인수가 있음 std::allocator<T>) 템플리트 템플리트 매개 변수 Container에는 하나만 있습니다. C ++ 17 ( CWG 150 ) 이후 , 템플리트 템플리트 인수 가 더 적은 템플리트 매개 변수로 템플리트 템플리트 매개 변수와 일치 하도록 기본 템플리트 인수가 허용 됩니다.

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 전에 템플릿 템플릿 매개 변수에 대한 기본 인수와 함께 2 템플릿 매개 변수를 정의 할 수 있습니다 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)

또는 매개 변수 팩을 적용하십시오 .

template<typename Iterator, template<typename...> class Container>
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)

1

C ++의 일부 버전에서는 실제로 Container일치하지 std::vector않기 때문에 일치 할 수 없습니다 . 그것은 A의 두 번째 매개 변수 (할당 자 유형) 기본 템플릿 인수를 가지고 어디에.std::vectortemplate <typename> classtemplate <typename, typename> class

다른 템플릿 매개 변수를 추가 typename Alloc하면 function 매개 변수를 만들 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>>>;

실제로 나는 항상 매개 변수에 제공된 컨테이너가 첫 번째 매개 변수 유형을 가진 std :: iterator 쌍으로 값을 전달하도록하고 싶습니다. 따라서 제공 한 템플릿 기능의 첫 번째 단순화가 내 요구 사항을 충족시킬 수 없습니다. SFINAE 솔루션이 두 번째로 할 것입니다. 어쨌든, 대단히 감사합니다
dannftk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.