การลดชนิดอาร์กิวเมนต์เท็มเพลตเทมเพลต 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.

เสียงดังกราวของเอาท์พุท:

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)

ฉันไม่จับอะไร การใช้เทมเพลตชนิดการหักแม่แบบของฉันไม่ถูกต้องและดูเหมือนว่าเป็นการละเมิดจากมุมมองมาตรฐานหรือไม่ ทั้งกรัม ++ - 9.2กับlistdc ++ 11หรือเสียงดังกราว ++กับlibc ++สามารถที่จะรวบรวมนี้


1
มันทำงานบน GCC กับ-std=c++17และบนเสียงดังกราวด้วย-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เพราะไม่ได้เป็นจริงstd::vector template <typename> classมันเป็นtemplate <typename, typename> classที่พารามิเตอร์ที่สอง (ประเภทตัวจัดสรร) มีอาร์กิวเมนต์แม่แบบเริ่มต้น

แม้ว่ามันจะทำงานเพื่อเพิ่มพารามิเตอร์เทมเพลตอื่นtypename Allocให้สร้างพารามิเตอร์ฟังก์ชันContainer<std::pair<Iterator, Iterator>, Alloc>ซึ่งอาจเป็นปัญหาสำหรับประเภทคอนเทนเนอร์อื่น

แต่เนื่องจากฟังก์ชันของคุณไม่ได้ใช้พารามิเตอร์เทมเพลตเทมเพลตContainerจึงไม่จำเป็นต้องมีการหักล้างอาร์กิวเมนต์เทมเพลตที่ซับซ้อนเช่นนี้ด้วย gotchas และข้อ จำกัด ทั้งหมดของการหักล้างอาร์กิวเมนต์เท็มเพลตเทมเพลต:

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 :: pair ของตัววนซ้ำที่มีชนิดของพารามิเตอร์แรกดังนั้นการทำให้ฟังก์ชั่นเทมเพลตแรกที่คุณเสนอให้ดูไม่ตรงกับความต้องการของฉันตรงกันข้าม ถึงตอนนี้วิธีที่สองที่ทางออกของคุณกับ SFINAE จะทำ อย่างไรก็ตามขอบคุณมาก
dannftk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.