Loại trừ đối số mẫu C ++


10

Tôi có mã tìm và in ra các mẫu trùng khớp khi đi qua vùng chứa các chuỗi. In được thực hiện trong chức năng foo được templated

Mật mã

#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;
}

Khi biên dịch, tôi đã gặp phải một lỗi rằng các kiểu khấu trừ đã thất bại do sự không nhất quán của các trình vòng lặp được cung cấp, các kiểu của chúng hóa ra rất đa dạng.

Lỗi biên dịch 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.

Đầu ra của 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)

Tôi không bắt được gì? Việc sử dụng các kiểu mẫu của tôi có bị sai và xuất hiện lạm dụng theo quan điểm của tiêu chuẩn không? Cả g ++ - 9.2 với listdc ++ 11 cũng không clang ++ với libc ++ đều có thể biên dịch cái này.


1
Nó hoạt động trên GCC với -std=c++17và trên Clang với -std=c++17-frelaxed-template-template-argscờ. Nếu không , có vẻ như bạn cần một tham số mẫu khác cho bộ cấp phát.
HolyBlackCat

@HolyBlackCat, thực sự, cảm ơn bạn
dannftk

Câu trả lời:


10

Mã của bạn sẽ hoạt động tốt kể từ C ++ 17. (Nó biên dịch với gcc10 .)

Đối số mẫu mẫu std::vectorcó hai tham số mẫu (đối số thứ 2 có đối số mặc định std::allocator<T>), nhưng tham số mẫu mẫu Containerchỉ có một. Kể từ C ++ 17 ( CWG 150 ), các đối số mẫu mặc định được phép cho đối số mẫu khuôn mẫu để khớp với tham số mẫu khuôn mẫu với ít tham số mẫu hơn.

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

Trước C ++ 17, bạn có thể xác định tham số mẫu thứ 2 với đối số mặc định cho tham số mẫu mẫu Container, ví dụ:

template<typename Iterator, template<typename T, typename Alloc=std::allocator<T>> class Container>
void foo(Iterator first, Container<std::pair<Iterator, Iterator>> const &findings)

Hoặc áp dụng gói tham số .

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

1

Trong một số phiên bản của C ++, Containerkhông thể phù hợp std::vector, vì std::vectorthực tế không phải là một template <typename> class. Đó là template <typename, typename> classnơi tham số thứ hai (loại cấp phát) có đối số mẫu mặc định.

Mặc dù nó có thể hoạt động để thêm một tham số mẫu khác tạo tham số typename Allochàm Container<std::pair<Iterator, Iterator>, Alloc>, đó có thể là một vấn đề đối với các loại vùng chứa khác.

Nhưng vì hàm của bạn không thực sự sử dụng tham số mẫu mẫu Container, nên không cần phải khấu trừ đối số mẫu phức tạp như vậy, với tất cả các vấn đề và giới hạn của việc suy luận đối số mẫu mẫu:

template<typename Iterator, class Container>
void foo(Iterator first, Container const &findings);

Điều này cũng không cần Iteratorphải được suy luận là cùng một loại ở ba nơi khác nhau. Có nghĩa là nó sẽ hợp lệ để vượt qua một X::iteratoras firstvà một container chứa X::const_iteratorhoặc ngược lại, và việc khấu trừ đối số khuôn mẫu vẫn có thể thành công.

Một nhược điểm nhỏ là nếu một mẫu khác sử dụng các kỹ thuật SFINAE để cố gắng xác định xem chữ ký của foocó hợp lệ hay không, tuyên bố đó sẽ khớp với hầu hết mọi thứ, như thế nào foo(1.0, 2). Điều này thường không quan trọng đối với chức năng cho mục đích cụ thể, nhưng thật tuyệt khi hạn chế hơn (hoặc "thân thiện với SFINAE") ít nhất là đối với các chức năng cho mục đích chung. Chúng ta có thể thêm một hạn chế cơ bản với một cái gì đó như:

// 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>>>;

Trên thực tế tôi luôn muốn đảm bảo rằng bộ chứa được cung cấp trong các tham số truyền tải các giá trị dưới dạng std :: cặp bộ lặp có loại tham số đầu tiên, do đó, đơn giản hóa đầu tiên của hàm mẫu mà bạn cung cấp dường như không thể đáp ứng yêu cầu của tôi, ngược lại Đây là giải pháp thứ hai của bạn với SFINAE sẽ làm. Dù sao, cảm ơn bạn rất nhiều
dannftk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.