C ++ şablon şablonu bağımsız değişken türleri kesinti


10

Ben bulur ve dizeleri konteyner üzerinde gidiyor gibi bir desen maçlar yazdırır kodu var. Baskı işlevi gerçekleştirilir foo şablonlu edilir

Kod

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

Derleme yapılırken yineleyicilerin tutarsızlığı nedeniyle tür kesinti işleminin başarısız olduğu konusunda bir hatayla karşılaştım, türlerinin farklı olduğu ortaya çıktı.

GCC derleme hatası:

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 çıktı:

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)

Ne yakalamıyorum? Şablon şablonu türlerinde kesinti kullanmam yanlış mı ve standardın bakış açısından kötüye kullanım olarak mı görünüyor? Ne g ++ - 9.2 ile listdc ++ 11 de çınlama ++ ile C kütüphanesi ++ bu derleme edebiliyoruz.


1
Bayraklı -std=c++17Clang ile ve Clang üzerinde çalışır -std=c++17-frelaxed-template-template-args. Aksi takdirde , ayırıcı için başka bir şablon parametresine ihtiyacınız var gibi görünüyor .
HolyBlackCat

@HolyBlackCat, gerçekten, teşekkür ederim
dannftk

Yanıtlar:


10

Kodunuz C ++ 17'den beri iyi çalışmalıdır. ( Gcc10 ile derlenir .)

Şablon şablonu bağımsız değişkeninin std::vectoriki şablon parametresi vardır (ikincisinin varsayılan bağımsız değişkeni vardır std::allocator<T>), ancak şablon şablonu parametresinin Containeryalnızca bir parametresi vardır. C ++ 17'den ( CWG 150 ) beri , şablon şablonu bağımsız değişkeninin şablon şablonu parametresini daha az şablon parametresiyle eşleştirmesi için varsayılan şablon bağımsız değişkenlerine izin verilir .

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'den önce, şablon şablonu parametresi için varsayılan argümanla 2. şablon parametresini tanımlayabilirsiniz Container, örn.

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

Veya parametre paketini uygulayın .

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

1

Bazı C ++ sürümlerinde, Containereşleşemez std::vector, çünkü std::vectoraslında bir değil template <typename> class. Bu template <typename, typename> class, ikinci parametrenin (ayırıcı türü) varsayılan bir şablon bağımsız değişkenine sahip olduğu yerdir.

typename Allocİşlev parametresini yapmak için başka bir şablon parametresi eklemek işe Container<std::pair<Iterator, Iterator>, Alloc>yarayabilse de, bu diğer kap türleri için bir sorun olabilir.

Ancak işleviniz aslında şablon şablonu parametresini kullanmadığından, Containerbir şablon şablonu argümanını çıkarmanın tüm sorunları ve sınırlamaları ile birlikte böyle karmaşık bir şablon bağımsız değişkeni kesintisine gerek yoktur:

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

Bunun Iteratorüç farklı yerde tam olarak aynı türden çıkarılması gerekmez . Yani bir X::iteratoras firstve bir kapsayıcı X::const_iteratorveya tam tersi içeren bir kapsayıcı geçmek geçerli olacaktır ve şablon bağımsız değişken kesinti yine de başarılı olabilir.

Tek dezavantajı, başka bir şablon, bir imzanın foogeçerli olup olmadığını belirlemek için SFINAE tekniklerini kullanırsa , bu bildirimin neredeyse her şeyle eşleşeceğidir foo(1.0, 2). Bu genellikle belirli bir amaçlı işlev için önemli değildir, ancak en azından genel amaçlı işlevler için daha kısıtlayıcı (veya "SFINAE dostu") olması güzeldir. Aşağıdaki gibi temel bir kısıtlama ekleyebiliriz:

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

Aslında her zaman parametrelerde sağlanan kabın değerleri ilk parametrenin türüne sahip std :: çift yineleyici olarak ilettiğinden emin olmak istiyorum, bu nedenle sunduğunuz şablon işlevinin ilk basitleştirmesi gereksinimlerimi karşılamıyor gibi görünmüyor buna ikinci SFINAE ile çözüm yapacağız. Neyse, çok teşekkür ederim
dannftk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.