web-dev-qa-db-fra.com

Déduction des types d'argument de modèle de modèle C ++

J'ai du code qui trouve et imprime les correspondances d'un modèle en passant par le conteneur de chaînes. L'impression est effectuée dans la fonction foo qui est modélisée

Le code

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

Lors de la compilation, j'ai eu une erreur indiquant que la déduction des types a échoué en raison de l'incohérence des itérateurs fournis, leurs types se révèlent être divers.

GCC erreur de compilation:

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 sortie:

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)

Qu'est-ce que je ne saisis pas? Est-ce que mon utilisation de la déduction des modèles de modèles est incorrecte et semble être un abus du point de vue de la norme? Ni g ++ - 9.2 with listdc ++ 11 ni clang ++ with libc ++ ne sont capables de compiler cela.

10
dannftk

Dans certaines versions de C++, Container ne peut pas correspondre à std::vector, Car std::vector N'est pas en fait un template <typename> class. C'est un template <typename, typename> class Où le deuxième paramètre (le type d'allocateur) a un argument modèle par défaut.

Bien que cela puisse fonctionner pour ajouter un autre paramètre de modèle typename Alloc Créer le paramètre de fonction Container<std::pair<Iterator, Iterator>, Alloc>, Cela pourrait être un problème pour d'autres types de conteneurs.

Mais comme votre fonction n'utilise pas réellement le paramètre de modèle de modèle Container, il n'est pas nécessaire d'exiger une déduction d'argument de modèle aussi compliquée, avec tous les pièges et limitations de la déduction d'un argument de modèle de modèle:

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

Cela ne nécessite pas non plus que Iterator soit déduit exactement du même type à trois endroits différents. Cela signifie qu'il sera valide de passer un X::iterator Comme first et un conteneur contenant X::const_iterator Ou vice versa, et la déduction de l'argument du modèle pourrait toujours réussir.

Le seul léger inconvénient est que si un autre modèle utilise les techniques SFINAE pour essayer de déterminer si une signature de foo est valide, cette déclaration correspondrait à presque tout, comme foo(1.0, 2). Ce n'est souvent pas important pour une fonction à usage spécifique, mais c'est bien d'être plus restrictif (ou "compatible avec SFINAE") au moins pour les fonctions à usage général. Nous pourrions ajouter une restriction de base avec quelque chose comme:

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