2016-08-13 3 views
2

J'essaye d'écrire une fonction modèle simple qui imprime chaque élément d'un conteneur, sans utiliser pour les boucles. Jusqu'à présent, j'aipassant la fonction de modèle à std :: for_each

#include <iostream> 
#include <vector> 
#include <algorithm> 

template <typename T> void print_with_space(T x){ 
    std::cout << x << ' '; 
} 

template <typename T> void print_all(T beg, T end){ 
    std::for_each(beg, end, print_with_space<int>); 
    std::cout << '\n'; 
} 

int main(){ 
    int a[] = {1, 2, 3}; 
    std::vector<int> v(a, a+3); 
    print_all(v.begin(), v.end()); 
    return 0; 
} 

Le code compile et fonctionne, mais seulement parce que je mis print_with_space<int> dans la mise en œuvre de print_all. Je voudrais simplement avoir print_with_space là pour des raisons évidentes, mais le code ne compile pas. Comment puis-je faire cela?

+1

Remplacer par '[&] (T const & x) {print_with_space (x);}' – 0x499602D2

+0

Je mets que le troisième argument for_each, et il n'a pas compiler avec g ++ -std = C++ 11 – xdavidliu

+0

[Voir cette proposition] (http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0091r1.html). –

Répondre

5

Vous pouvez utiliser:

std::for_each(beg, end, [](const typename T::value_type& value) { 
    print_with_space(value); 
}); 

T est de type std::vector<>::iterator, qui est un RandomAccessIterator. Chaque RandomAcessIterator a un type sous-jacent, qui est exposé par value_type. Par conséquent, si vous transmettez std::vector<int>::iterator, std::vector<int>::iterator::value_type sera int.

Maintenant que vous avez le type, vous pouvez créer un lambda, qui sera exécuté à chaque itération.


En C++ 14, vous pouvez même le faire:

//'auto' automatically deduces the type for you 
std::for_each(beg, end, [](const auto& value) { 
    print_with_space(value); 
}); 
+0

J'ai dû mettre 'typename' avant le T :: value_type:' std :: for_each (beg, fin, [] (nom de type T :: valeur_type & val) {print_with_space (val);}); 'et cela a fonctionné avec std = C++ 11 – xdavidliu

+0

@xdavidliu Pour moi cela a fonctionné sans 'typename', eh bien :) – Rakete1111

+0

En fait les itérateurs n'ont pas besoin de définir un alias de type' value_type'. En fait, les pointeurs satisfont le concept 'RandomAccessIterator', mais ils n'ont évidemment aucun type de membre. Il vaudrait peut-être mieux passer en C++ 14 et faire du lambda polymorphe, si vous ne voulez pas vous tromper avec les traits de l'itérateur. –

1

Alternative pour 03 C++:

#include <iterator> 

template <typename T> void print_all(T beg, T end) 
{ 
    typedef typename std::iterator_traits<T>::value_type val_t; 
    std::for_each(beg, end, print_with_space<val_t>); 
    std::cout << '\n'; 
} 
+0

cela fonctionne. Je viens de réaliser que je peux aussi faire 'std :: for_each (beg, fin, print_with_space );' et cela fonctionne pour C++ 03 – xdavidliu

+0

@xdavidliu comme je l'ai mentionné dans un autre commentaire de réponse, les itérateurs pas besoin de déclarer 'T :: value_type'. Pensez aux pointeurs (et que dans les configurations de version, les itérateurs vectoriels sont parfois des pointeurs). Il est préférable d'utiliser 'iterator_traits', qui devrait fonctionner avec tous les itérateurs conformes. –

+0

l'ai eu; 'print_with_space :: value_type>' permet de faire 'int a [] = {1, 2, 3}; print_all (a, a + 3); ', ce qui ne serait pas possible avec la première méthode – xdavidliu

1

Une autre option:

template <typename T> void print_all(T beg, T end) { 
    std::for_each(beg, end, print_with_space<decltype(*beg)>); 
    std::cout << '\n'; 
} 
1

Le solution la plus flexible, qui fonctionnera avec toutes les versions de C++, est de faire print_with_space un objet fonction.

Cela confère un certain nombre d'avantages:

  1. pas besoin de spécifier le type de modèle sur le site d'appel.
  2. pas besoin de bidouiller avec la déduction manuelle.
  3. une spécialisation partielle peut être obtenue en faisant en sorte que le foncteur soit déféré à une fonction libre modélisée.

Tels que:

#include <iostream> 
#include <iomanip> 
#include <vector> 
#include <algorithm> 

// basic implementation 
template<class T> void impl_print_with_space(const T& x) 
{ 
    std::cout << x << ' '; 
} 

// what about special handling for strings? 

template<class C, class Ch, class Alloc> 
void impl_print_with_space(const std::basic_string<C, Ch, Alloc>& x) 
{ 
    std::cout << std::quoted(x) << ' '; 
} 

// functor 

struct print_with_space 
{ 
    template<class T> void operator()(const T& x) const 
    { 
    impl_print_with_space(x); 
    } 
}; 


template <typename Iter> void print_all(Iter beg, Iter end) 
{ 
    std::for_each(beg, end, print_with_space()); 
    std::cout << '\n'; 
} 

int main(){ 
    int a[] = {1, 2, 3}; 
    std::vector<int> v(a, a+3); 
    print_all(v.begin(), v.end()); 

    auto b = std::vector<std::string> { "hello", "world" }; 
    print_all(b.begin(), b.end()); 

    return 0; 
}